text: /* Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #define C_CFISH_BYTEBUF
  #define C_CFISH_VIEWBYTEBUF
  #define CFISH_USE_SHORT_NAMES
  
  #include <stdlib.h>
  #include <string.h>
  #include <stdio.h>
  #include <ctype.h>
  
  #include "Clownfish/Class.h"
  #include "Clownfish/ByteBuf.h"
  #include "Clownfish/Err.h"
  #include "Clownfish/Util/Memory.h"
  
  static void
  S_grow(ByteBuf *self, size_t size);
  
  ByteBuf*
  BB_new(size_t capacity) {
      ByteBuf *self = (ByteBuf*)Class_Make_Obj(BYTEBUF);
      return BB_init(self, capacity);
  }
  
  ByteBuf*
  BB_init(ByteBuf *self, size_t capacity) {
      size_t amount = capacity ? capacity : sizeof(int64_t);
      self->buf   = NULL;
      self->size  = 0;
      self->cap   = 0;
      S_grow(self, amount);
      return self;
  }
  
  ByteBuf*
  BB_new_bytes(const void *bytes, size_t size) {
      ByteBuf *self = (ByteBuf*)Class_Make_Obj(BYTEBUF);
      BB_init(self, size);
      memcpy(self->buf, bytes, size);
      self->size = size;
      return self;
  }
  
  ByteBuf*
  BB_new_steal_bytes(void *bytes, size_t size, size_t capacity) {
      ByteBuf *self = (ByteBuf*)Class_Make_Obj(BYTEBUF);
      return BB_init_steal_bytes(self, bytes, size, capacity);
  }
  
  ByteBuf*
  BB_init_steal_bytes(ByteBuf *self, void *bytes, size_t size,
                      size_t capacity) {
      self->buf  = (char*)bytes;
      self->size = size;
      self->cap  = capacity;
      return self;
  }
  
  void
  BB_Destroy_IMP(ByteBuf *self) {
      FREEMEM(self->buf);
      SUPER_DESTROY(self, BYTEBUF);
  }
  
  ByteBuf*
  BB_Clone_IMP(ByteBuf *self) {
      return BB_new_bytes(self->buf, self->size);
  }
  
  void
  BB_Set_Size_IMP(ByteBuf *self, size_t size) {
      if (size > self->cap) {
          THROW(ERR, "Can't set size to %u64 (greater than capacity of %u64)",
                (uint64_t)size, (uint64_t)self->cap);
      }
      self->size = size;
  }
  
  char*
  BB_Get_Buf_IMP(ByteBuf *self) {
      return self->buf;
  }
  
  size_t
  BB_Get_Size_IMP(ByteBuf *self) {
      return self->size;
  }
  
  size_t
  BB_Get_Capacity_IMP(ByteBuf *self) {
      return self->cap;
  }
  
  static CFISH_INLINE bool
  SI_equals_bytes(ByteBuf *self, const void *bytes, size_t size) {
      if (self->size != size) { return false; }
      return (memcmp(self->buf, bytes, self->size) == 0);
  }
  
  bool
  BB_Equals_IMP(ByteBuf *self, Obj *other) {
      ByteBuf *const twin = (ByteBuf*)other;
      if (twin == self)              { return true; }
      if (!Obj_Is_A(other, BYTEBUF)) { return false; }
      return SI_equals_bytes(self, twin->buf, twin->size);
  }
  
  bool
  BB_Equals_Bytes_IMP(ByteBuf *self, const void *bytes, size_t size) {
      return SI_equals_bytes(self, bytes, size);
  }
  
  int32_t
  BB_Hash_Sum_IMP(ByteBuf *self) {
      uint32_t       sum = 5381;
      uint8_t *const buf = (uint8_t*)self->buf;
  
      for (size_t i = 0, max = self->size; i < max; i++) {
          sum = ((sum << 5) + sum) ^ buf[i];
      }
  
      return (int32_t)sum;
  }
  
  static CFISH_INLINE void
  SI_mimic_bytes(ByteBuf *self, const void *bytes, size_t size) {
      if (size > self->cap) { S_grow(self, size); }
      memmove(self->buf, bytes, size);
      self->size = size;
  }
  
  void
  BB_Mimic_Bytes_IMP(ByteBuf *self, const void *bytes, size_t size) {
      SI_mimic_bytes(self, bytes, size);
  }
  
  void
  BB_Mimic_IMP(ByteBuf *self, Obj *other) {
      ByteBuf *twin = (ByteBuf*)CERTIFY(other, BYTEBUF);
      SI_mimic_bytes(self, twin->buf, twin->size);
  }
  
  static CFISH_INLINE void
  SI_cat_bytes(ByteBuf *self, const void *bytes, size_t size) {
      const size_t new_size = self->size + size;
      if (new_size > self->cap) {
          S_grow(self, Memory_oversize(new_size, sizeof(char)));
      }
      memcpy((self->buf + self->size), bytes, size);
      self->size = new_size;
  }
  
  void
  BB_Cat_Bytes_IMP(ByteBuf *self, const void *bytes, size_t size) {
      SI_cat_bytes(self, bytes, size);
  }
  
  void
  BB_Cat_IMP(ByteBuf *self, ByteBuf *other) {
      SI_cat_bytes(self, other->buf, other->size);
  }
  
  static void
  S_grow(ByteBuf *self, size_t size) {
      if (size > self->cap) {
          size_t amount    = size;
          size_t remainder = amount % sizeof(int64_t);
          if (remainder) {
              amount += sizeof(int64_t);
              amount -= remainder;
          }
          self->buf = (char*)REALLOCATE(self->buf, amount);
          self->cap = amount;
      }
  }
  
  char*
  BB_Grow_IMP(ByteBuf *self, size_t size) {
      if (size > self->cap) { S_grow(self, size); }
      return self->buf;
  }
  
  int
  BB_compare(const void *va, const void *vb) {
      ByteBuf *a = *(ByteBuf**)va;
      ByteBuf *b = *(ByteBuf**)vb;
      const size_t size = a->size < b->size ? a->size : b->size;
  
      int32_t comparison = memcmp(a->buf, b->buf, size);
  
      if (comparison == 0 && a->size != b->size) {
          comparison = a->size < b->size ? -1 : 1;
      }
  
      return comparison;
  }
  
  int32_t
  BB_Compare_To_IMP(ByteBuf *self, Obj *other) {
      CERTIFY(other, BYTEBUF);
      return BB_compare(&self, &other);
  }
  
  /******************************************************************/
  
  ViewByteBuf*
  ViewBB_new(char *buf, size_t size) {
      ViewByteBuf *self = (ViewByteBuf*)Class_Make_Obj(VIEWBYTEBUF);
      return ViewBB_init(self, buf, size);
  }
  
  ViewByteBuf*
  ViewBB_init(ViewByteBuf *self, char *buf, size_t size) {
      self->cap  = 0;
      self->buf  = buf;
      self->size = size;
      return self;
  }
  
  void
  ViewBB_Destroy_IMP(ViewByteBuf *self) {
      Obj_Destroy_t super_duper_destroy = METHOD_PTR(OBJ, CFISH_Obj_Destroy);
      super_duper_destroy((Obj*)self);
  }
  
  void
  ViewBB_Assign_Bytes_IMP(ViewByteBuf *self, char*buf, size_t size) {
      self->buf  = buf;
      self->size = size;
  }
  
  void
  ViewBB_Assign_IMP(ViewByteBuf *self, ByteBuf *other) {
      self->buf  = other->buf;
      self->size = other->size;
  }
  
  
  