#include "./packetbuf.h"

#include <sys/stat.h>
#include <zlib.h>

#include "./packetbuf_types.h"

packet_stats packet::stats;

// ----------------------------------------------------------------------

packet_contents *packet::alloc_contents(size_t alloc)
{
  stats.alloc_count++;

  size_t roundup_alloc = ((alloc + sizeof(packet_contents) + 1023) & ~1023) - sizeof(packet_contents);
  auto contents = reinterpret_cast<packet_contents *>(malloc(sizeof(packet_contents) + roundup_alloc));
  contents->refcnt = 1;
  contents->alloc = roundup_alloc;
  return contents;
}

void packet::decref(packet_contents *&it)
{
  if (!it) return;
  stats.decref_count++;

  int newrefs = --it->refcnt;
  if (newrefs == 0) {
    stats.free_count++;
    free(it);
    it = nullptr;
  }
}

void packet::decref(packet_annotations *&it)
{
  if (!it) return;
  stats.decref_count++;

  int newrefs = --it->refcnt;
  if (newrefs == 0) {
    stats.free_count++;
    delete it;
    it = nullptr;
  }
}

void packet::incref(packet_contents *it)
{
  it->refcnt++;
  stats.incref_count++;
}

void packet::incref(packet_annotations *it)
{
  if (!it) return;
  it->refcnt++;
  stats.incref_count++;
}

void packet::reserve_full(size_t new_size)
{
  if (new_size > contents->alloc || contents->refcnt > 1) {
    if (new_size > size_t(0x3fffffff))
      throw runtime_error("packet::reserve too large ("s + to_string(new_size) + ")"s);
    packet_contents *old_contents = contents;

    size_t new_alloc = old_contents->alloc;
    if (new_size > new_alloc) {
      new_alloc = max(new_size, new_alloc * 2);
      stats.expand_count++;
    }
    else {
      stats.cow_count++;
    }
    contents = alloc_contents(new_alloc);

    memcpy(contents->buf, old_contents->buf, old_contents->alloc);
    stats.copy_bytes_count += old_contents->alloc;

    decref(old_contents);
  }
}

// ----------------------------------------------------------------------

packet::packet() : contents(alloc_contents(1000)), annotations(nullptr), rd_pos(0), wr_pos(0) {}

packet::packet(u_char const *data, size_t size)
    : contents(alloc_contents(size)), annotations(nullptr), rd_pos(0), wr_pos(size)
{
  memcpy(contents->buf, data, size);
}

packet::packet(string const &data)
    : contents(alloc_contents(data.size())), annotations(nullptr), rd_pos(0), wr_pos(data.size())
{
  memcpy(contents->buf, data.data(), data.size());
}

packet::packet(size_t size) : contents(alloc_contents(size)), annotations(nullptr), rd_pos(0), wr_pos(0) {}

packet::packet(const packet &other)
    : contents(other.contents), annotations(other.annotations), rd_pos(other.rd_pos), wr_pos(other.wr_pos)
{
  incref(annotations);
  incref(contents);
}

packet &packet::operator=(packet const &other)
{
  assert(this != &other);

  decref(contents);
  contents = other.contents;
  incref(contents);

  decref(annotations);
  annotations = other.annotations;
  incref(annotations);

  rd_pos = other.rd_pos;
  wr_pos = other.wr_pos;
  return *this;
}

packet::~packet()
{
  decref(contents);
  decref(annotations);
}

size_t packet::size() const
{
  return wr_pos;
}
ssize_t packet::remaining() const
{
  return wr_pos - rd_pos;
}

string packet::as_string()
{
  return string(reinterpret_cast<char *>(wr_ptr()), (size_t)remaining());
}

packet packet::get_remainder()
{
  packet ret;
  ret.add_bytes(rd_ptr(), remaining());
  return ret;
}

size_t packet::size_bits() const
{
  return wr_pos * 8;
}
float packet::size_kbits() const
{
  return wr_pos * (8.0f / 1024.0f);
}
size_t packet::alloc() const
{
  return contents->alloc;
}

u_char const *packet::wr_ptr() const
{
  return contents->buf + wr_pos;
}
u_char const *packet::rd_ptr() const
{
  return contents->buf + rd_pos;
}
u_char const *packet::ptr() const
{
  return contents->buf;
}
u_char const *packet::begin() const
{
  return contents->buf;
}
u_char const *packet::end() const
{
  return contents->buf + wr_pos;
}
u_char packet::operator[](int index) const
{
  return ptr()[index];
}

u_char *packet::wr_ptr()
{
  return contents->buf + wr_pos;
}
u_char *packet::rd_ptr()
{
  return contents->buf + rd_pos;
}
u_char *packet::ptr()
{
  return contents->buf;
}
u_char *packet::begin()
{
  return contents->buf;
}
u_char *packet::end()
{
  return contents->buf + wr_pos;
}
u_char &packet::operator[](int index)
{
  return ptr()[index];
}

bool operator==(packet const &a, packet const &b)
{
  if (a.size() != b.size()) return false;
  if (memcmp(a.ptr(), b.ptr(), a.size()) != 0) return false;
  return true;
}

string &packet::annotation(string const &key)
{
  if (!annotations) {
    annotations = new packet_annotations;
    annotations->refcnt = 1;
  }
  return (annotations->table)[key];
}

string packet::annotation(string const &key) const
{
  if (annotations) {
    auto slot = annotations->table.find(key);
    if (slot != annotations->table.end()) {
      return (*slot).second;
    }
  }
  return "";
}

bool packet::has_annotation(string const &key) const
{
  if (annotations) {
    auto slot = annotations->table.find(key);
    if (slot != annotations->table.end()) {
      return true;
    }
  }
  return false;
}

void packet::resize(size_t newsize)
{
  reserve(newsize);
  wr_pos = newsize;
}

void packet::make_mutable()
{
  reserve(wr_pos);
}

void packet::clear()
{
  rd_pos = wr_pos = 0;
}

int packet::to_file(int fd) const
{
  int todo = size();
  int nw = write(fd, ptr(), todo);
  return nw;
}

int packet::to_file(FILE *fp) const
{
  U32 todo = size();
  int nw = fwrite(ptr(), 1, todo, fp);
  return nw;
}

void packet::to_file_boxed(int fd) const
{
  U32 todo = size();
  int nw_todo = write(fd, &todo, sizeof(todo));
  int nw_ptr = write(fd, ptr(), todo);
  if (nw_todo < 0 || nw_ptr < 0) throw runtime_error("to_file_boxed: write: "s + strerror(errno));
  if (U32(nw_ptr) != todo) throw runtime_error("to_file_boxed: short write");
}

void packet::to_gzfile_boxed(gzFile_s *fp) const
{
  U32 todo = size();
  int nw_todo = gzwrite(fp, &todo, sizeof(todo));
  int nw_ptr = gzwrite(fp, ptr(), todo);
  if (nw_todo < 0 || nw_ptr < 0) {
    int errnum = 0;
    throw runtime_error("to_gzfile_boxed: write: "s + gzerror(fp, &errnum));
  }
  if (U32(nw_ptr)!= todo) throw runtime_error("to_gzfile_boxed: short write");
}

void packet::write_to_file(char const *fn) const
{
  int fd = open(fn, O_WRONLY|O_CREAT, 0777);
  if (fd < 0) throw runtime_error(""s + fn + ": open failed: " + strerror(errno));
  int todo = size();
  int nw = write(fd, ptr(), todo);
  if (nw != todo) throw runtime_error(""s + fn + ": partial write");
  if (close(fd) < 0) {
    throw runtime_error(""s + fn + ": close: " + strerror(errno));
  }
}


int packet::rd_to_file(int fd) const
{
  ssize_t todo = remaining();
  int nw = write(fd, rd_ptr(), todo);
  return nw;
}

int packet::rd_to_file(FILE *fp) const
{
  ssize_t todo = remaining();
  int nw = fwrite(rd_ptr(), 1, todo, fp);
  return nw;
}

ssize_t packet::add_read(int fd, size_t readsize)
{
  reserve(wr_pos + readsize);
  ssize_t nr = read(fd, wr_ptr(), readsize);
  if (nr > 0) {
    wr_pos += nr;
  }
  return nr;
}

ssize_t packet::add_pread(int fd, size_t readsize, off_t offset)
{
  reserve(wr_pos + readsize);
  ssize_t nr = pread(fd, wr_ptr(), readsize, offset);
  if (nr > 0) {
    wr_pos += nr;
  }
  return nr;
}

ssize_t packet::add_read(FILE *fp, size_t readsize)
{
  reserve(wr_pos + readsize);
  ssize_t nr = fread(wr_ptr(), 1, readsize, fp);
  if (nr > 0) {
    wr_pos += nr;
  }
  return nr;
}

void packet::add_file_contents(int fd)
{
  while (1) {
    int nr = add_read(fd, 65536);
    if (nr < 0) throw runtime_error("packet::from_file");
    if (nr == 0) break;
  }
}

void packet::add_file_contents(FILE *fp)
{
  while (1) {
    int nr = add_read(fp, 8192);
    if (nr <= 0) break;
  }
}

void packet::add_bytes(u_char const *data, size_t size)
{
  reserve(wr_pos + size);
  memcpy(contents->buf + wr_pos, data, size);
  wr_pos += size;
}

void packet::add_bytes(char const *data, size_t size)
{
  add_bytes(reinterpret_cast<u_char const *>(data), size);
}

void packet::add_reversed(u_char const *data, size_t size)
{
  reserve(wr_pos + size);
  auto p = contents->buf + wr_pos;
  for (size_t i = 0; i < size; i++) {
    p[i] = data[size - 1 - i];
  }
  wr_pos += size;
}

void packet::add_nl_string(const char *s)
{
  size_t slen = strlen(s);
  add_bytes(s, slen);
  add_bytes("\n", 1);
}

void packet::add_nl_string(string const &s)
{
  size_t slen = s.size();
  add_bytes(s.data(), slen);
  add_bytes("\n", 1);
}

void packet::add_len8_string(string const &s)
{
  if (s.size() > 255) {
    throw runtime_error("string too long");
  }
  add_be_uint8((U8)s.size());
  add_bytes(s.data(), s.size());
}

void packet::add_remainder_string(string const &s)
{
  add_bytes(s.data(), s.size());
}

void packet::add_pkt(packet const &wr)
{
  add(static_cast<u_int>(wr.remaining()));
  add_bytes(wr.rd_ptr(), wr.remaining());
}

void packet::add_be_uint64(uint64_t x)
{
  u_char buf[8];
  buf[0] = (x >> 56) & 0xff;
  buf[1] = (x >> 48) & 0xff;
  buf[2] = (x >> 40) & 0xff;
  buf[3] = (x >> 32) & 0xff;
  buf[4] = (x >> 24) & 0xff;
  buf[5] = (x >> 16) & 0xff;
  buf[6] = (x >> 8) & 0xff;
  buf[7] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_be_uint32(u_int x)
{
  u_char buf[4];
  buf[0] = (x >> 24) & 0xff;
  buf[1] = (x >> 16) & 0xff;
  buf[2] = (x >> 8) & 0xff;
  buf[3] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_be_uint24(u_int x)
{
  u_char buf[3];
  buf[0] = (x >> 16) & 0xff;
  buf[1] = (x >> 8) & 0xff;
  buf[2] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_be_uint16(u_int x)
{
  u_char buf[2];
  buf[0] = (x >> 8) & 0xff;
  buf[1] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_be_uint8(u_int x)
{
  u_char buf[1];
  buf[0] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_be_double(double x)
{
  union {
    u_char bytes[8];
    double value;
  } it;

  it.value = x;

#if BYTE_ORDER == LITTLE_ENDIAN
  add_reversed(it.bytes, 8);
#elif BYTE_ORDER == BIG_ENDIAN
  add_bytes(it.bytes, 8);
#else
#error "unexpected byte order"
#endif
}

void packet::add_be_float(float x)
{
  union {
    u_char bytes[4];
    float value;
  } it;

  it.value = x;

#if BYTE_ORDER == LITTLE_ENDIAN
  add_reversed(it.bytes, 4);
#elif BYTE_ORDER == BIG_ENDIAN
  add_bytes(it.bytes, 4);
#else
#error "unexpected byte order"
#endif
}

void packet::add_le_uint64(uint64_t x)
{
  u_char buf[8];
  buf[0] = (x >> 0) & 0xff;
  buf[1] = (x >> 8) & 0xff;
  buf[2] = (x >> 16) & 0xff;
  buf[3] = (x >> 24) & 0xff;
  buf[4] = (x >> 32) & 0xff;
  buf[5] = (x >> 40) & 0xff;
  buf[6] = (x >> 48) & 0xff;
  buf[7] = (x >> 56) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_le_uint32(u_int x)
{
  u_char buf[4];
  buf[0] = (x >> 0) & 0xff;
  buf[1] = (x >> 8) & 0xff;
  buf[2] = (x >> 16) & 0xff;
  buf[3] = (x >> 24) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_le_uint24(u_int x)
{
  u_char buf[3];
  buf[0] = (x >> 0) & 0xff;
  buf[1] = (x >> 8) & 0xff;
  buf[2] = (x >> 16) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_le_uint16(u_int x)
{
  u_char buf[2];
  buf[0] = (x >> 0) & 0xff;
  buf[1] = (x >> 8) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_le_uint8(u_int x)
{
  u_char buf[1];
  buf[0] = (x >> 0) & 0xff;
  add_bytes(buf, sizeof(buf));
}

void packet::add_le_double(double x)
{
  union {
    u_char bytes[8];
    double value;
  } it;

  it.value = x;

#if BYTE_ORDER == BIG_ENDIAN
  add_reversed(it.bytes, 8);
#elif BYTE_ORDER == LITTLE_ENDIAN
  add_bytes(it.bytes, 8);
#else
#error "unexpected byte order"
#endif
}

void packet::add_le_float(float x)
{
  union {
    u_char bytes[4];
    float value;
  } it;

  it.value = x;

#if BYTE_ORDER == BIG_ENDIAN
  add_reversed(it.bytes, 4);
#elif BYTE_ORDER == LITTLE_ENDIAN
  add_bytes(it.bytes, 4);
#else
#error "unexpected byte order"
#endif
}

void packet::dump(FILE *fp) const
{
  for (int i = 0; i < (int)wr_pos;) {
    fprintf(fp, "%04x: ", i);
    int todo = min(16, (int)(wr_pos - i));
    for (int j = i; j < i + todo; j++) {
      fprintf(fp, " %02x", (int)(u_char)contents->buf[j]);
    }
    fprintf(fp, "   ");
    for (int j = i; j < i + todo; j++) {
      fprintf(fp, "%s", charname_hex((u_char)contents->buf[j]));
    }
    fprintf(fp, "\n");
    i += todo;
  }
}

string packet::dump_hex() const
{
  string ret = "[";
  for (int i = 0; i < (int)wr_pos; i++) {
    if (i > 0) ret += " ";
    char buf[10];
    snprintf(buf, 10, "%02x", (int)(u_char)contents->buf[i]);
    ret += buf;
  }
  ret += "] size=";
  ret += to_string(wr_pos);
  return ret;
}

// ----------------------------------------------------------------------

void packet::rewind()
{
  rd_pos = 0;
}

void packet::get_skip(int n)
{
  rd_pos += n;
}

bool packet::get_test(u_char *data, size_t size)
{
  if ((int)rd_pos + (int)size <= (int)wr_pos) {
    memcpy(data, contents->buf + rd_pos, size);
    rd_pos += size;
    return true;
  }
  return false;
}

void packet::get_bytes(u_char *data, size_t size)
{
  if (!get_test(data, size)) {
    throw packet_rd_overrun_err(size - remaining());
  }
}

void packet::get_bytes(char *data, size_t size)
{
  get_bytes(reinterpret_cast<u_char *>(data), size);
}

void packet::get_reversed(u_char *data, size_t size)
{
  for (size_t i = 0; i < size; i++) {
    get_bytes(&data[size - 1 - i], 1);
  }
}

packet packet::get_pkt()
{
  u_int len;
  get(len);

  if ((ssize_t)len > remaining()) {
    printf("packet_rd_overrun_err needed=%ld had=%ld\n", (long)len, (long)remaining());
    throw packet_rd_overrun_err(len - remaining());
  }
  packet ret(*this);

  ret.wr_pos = ret.rd_pos + len;
  get_skip(len);
  return ret;
}

bool packet::get_bool()
{
  bool ret{false};
  packetio::packet_rd_value(*this, ret);
  return ret;
}

char packet::get_char()
{
  char ret{0};
  packetio::packet_rd_value(*this, ret);
  return ret;
}

uint64_t packet::get_be_uint64()
{
  u_char buf[8];
  get_bytes(buf, 8);

  return (
      ((U64)buf[0] << 56) | ((U64)buf[1] << 48) | ((U64)buf[2] << 40) | ((U64)buf[3] << 32) | ((U64)buf[4] << 24)
      | ((U64)buf[5] << 16) | ((U64)buf[6] << 8) | ((U64)buf[7] << 0));
}

uint32_t packet::get_be_uint32()
{
  u_char buf[4];
  get_bytes(buf, 4);

  return (((u_int)buf[0] << 24) | ((u_int)buf[1] << 16) | ((u_int)buf[2] << 8) | ((u_int)buf[3] << 0));
}

uint32_t packet::get_be_uint24()
{
  u_char buf[3];
  get_bytes(buf, 3);

  return (((u_int)buf[0] << 16) | ((u_int)buf[1] << 8) | ((u_int)buf[2] << 0));
}

uint16_t packet::get_be_uint16()
{
  u_char buf[2];
  get_bytes(buf, 2);

  return (((u_short)buf[0] << 8) | ((u_short)buf[1] << 0));
}

uint8_t packet::get_be_uint8()
{
  u_char buf[1];
  get_bytes(buf, 1);

  return (u_char)buf[0];
}

int64_t packet::get_be_int64()
{
  return (int64_t)get_be_uint64();
}

int32_t packet::get_be_int32()
{
  return (int32_t)get_be_uint32();
}

int32_t packet::get_be_int24()
{
  uint32_t us = get_be_uint24();
  return (int)(us << 8) >> 8; // sign extend
}

int16_t packet::get_be_int16()
{
  return (int16_t) get_be_uint16();
}

int8_t packet::get_be_int8()
{
  return (int8_t)get_be_uint8();
}


double packet::get_be_double()
{
  union {
    u_char bytes[8];
    double value;
  } it;

#if BYTE_ORDER == LITTLE_ENDIAN
  get_reversed(it.bytes, 8);
#elif BYTE_ORDER == BIG_ENDIAN
  get_bytes(it.bytes, 8);
#else
#error "unexpected byte order"
#endif

  return it.value;
}

float packet::get_be_float()
{
  union {
    u_char bytes[4];
    float value;
  } it;

#if BYTE_ORDER == LITTLE_ENDIAN
  get_reversed(it.bytes, 4);
#elif BYTE_ORDER == BIG_ENDIAN
  get_bytes(it.bytes, 4);
#else
#error "unexpected byte order"
#endif

  return it.value;
}

uint64_t packet::get_le_uint64()
{
  u_char buf[8];
  get_bytes(buf, 8);

  return (
      ((uint64_t)buf[0] << 0) | ((uint64_t)buf[1] << 8) | ((uint64_t)buf[2] << 16) | ((uint64_t)buf[3] << 24)
      | ((uint64_t)buf[4] << 32) | ((uint64_t)buf[5] << 40) | ((uint64_t)buf[6] << 48) | ((uint64_t)buf[7] << 56));
}

uint32_t packet::get_le_uint32()
{
  u_char buf[4];
  get_bytes(buf, 4);

  return (((u_int)buf[0] << 0) | ((u_int)buf[1] << 8) | ((u_int)buf[2] << 16) | ((u_int)buf[3] << 24));
}

uint32_t packet::get_le_uint24()
{
  u_char buf[3];
  get_bytes(buf, 3);

  return (((u_int)buf[0] << 0) | ((u_int)buf[1] << 8) | ((u_int)buf[2] << 16));
}

uint16_t packet::get_le_uint16()
{
  u_char buf[2];
  get_bytes(buf, 2);

  return (((u_short)buf[0] << 0) | ((u_short)buf[1] << 8));
}

uint8_t packet::get_le_uint8()
{
  u_char buf[1];
  get_bytes(buf, 1);

  return (u_char)buf[0];
}



int64_t packet::get_le_int64()
{
  return (int64_t)get_le_uint64();
}

int32_t packet::get_le_int32()
{
  return (int32_t)get_le_uint32();
}

int32_t packet::get_le_int24()
{
  uint32_t us = get_le_uint24();
  return (int32_t)(us << 8) >> 8; // sign extend
}

int16_t packet::get_le_int16()
{
  return (int16_t)get_le_uint16();
}

int8_t packet::get_le_int8()
{
  return (int8_t)get_le_uint8();
}



double packet::get_le_double()
{
  union {
    u_char bytes[8];
    double value;
  } it;

#if BYTE_ORDER == BIG_ENDIAN
  get_reversed(it.bytes, 8);
#elif BYTE_ORDER == LITTLE_ENDIAN
  get_bytes(it.bytes, 8);
#else
#error "unexpected byte order"
#endif

  return it.value;
}

float packet::get_le_float()
{
  union {
    u_char bytes[4];
    float value;
  } it;

#if BYTE_ORDER == BIG_ENDIAN
  get_reversed(it.bytes, 4);
#elif BYTE_ORDER == LITTLE_ENDIAN
  get_bytes(it.bytes, 4);
#else
#error "unexpected byte order"
#endif

  return it.value;
}

string packet::get_nl_string()
{
  string ret;
  while (rd_pos < wr_pos) {
    u_char c = contents->buf[rd_pos++];
    if (c == '\n') break;
    ret.push_back(c);
  }
  return ret;
}

string packet::get_len8_string()
{
  auto size = static_cast<size_t >(fget< u_char>());
  if (ssize_t(size) > remaining()) {
    throw packet_rd_overrun_err(size - remaining());
  }

  string ret((char const *)contents->buf + rd_pos, size);
  rd_pos += size;
  return ret;
}

string packet::get_lenbe16_string()
{
  auto size = get_be_uint16();
  if (size > remaining()) {
    throw packet_rd_overrun_err(size - remaining());
  }

  string ret((char const *)contents->buf + rd_pos, size);
  rd_pos += size;
  return ret;
}

string packet::get_lenbe32_string()
{
  auto size = ssize_t(get_be_uint32());
  if (size > remaining()) {
    throw packet_rd_overrun_err(size - remaining());
  }

  string ret((char const *)contents->buf + rd_pos, size);
  rd_pos += size;
  return ret;
}

string packet::get_string(size_t size)
{
  if (ssize_t(size) > remaining()) {
    throw packet_rd_overrun_err(size - remaining());
  }

  string ret((char const *)contents->buf + rd_pos, size);
  rd_pos += size;
  return ret;
}

string packet::get_remainder_string()
{
  string ret((char const *)contents->buf + rd_pos, wr_pos - rd_pos);
  rd_pos = wr_pos;
  return ret;
}

void packet::check_at_end()
{
  if (remaining() != 0) {
    throw packet_rd_overrun_err(0 - remaining());
  }
}

void packet::add_literal_typetag(char const *tag)
{
  size_t size = strlen(tag);
  if (!(size < 255)) {
    throw runtime_error("add_literal_typetag: tag too long (len="s + to_string(size) + ")"s);
    return;
  }
  add((u_char)size);
  add_bytes(tag, size);
}

ostream &operator<<(ostream &s, packet const &it)
{
  s << "packet(" << it.size() << " bytes)";
  // WRITEME: if size < 100, print in hex
  return s;
}

// ----------------------------------------------------------------------

packet packet::from_file_boxed(int fd)
{
  U32 todo;
  int nr = read(fd, &todo, sizeof(todo));
  if (nr < 0) throw runtime_error("from_file_boxed nr="s + to_string(nr) + " errno="s + strerror(errno));
  if (nr == 0) return packet(); // EOF
  if (nr != sizeof(todo)) throw runtime_error("from_file_boxed: short read 1");

  packet ret(todo + 32);
  nr = read(fd, ret.ptr(), todo);
  if (nr < 0) throw runtime_error("from_file_boxed: read");
  if (U32(nr) != todo) throw runtime_error("from_file_boxed: short read 2");
  ret.wr_pos += nr;

  return ret;
}

packet packet::from_gzfile_boxed(gzFile_s *fp)
{
  U32 todo;
  int nr = gzread(fp, &todo, sizeof(todo));
  if (nr < 0) {
    int errnum = 0;
    throw runtime_error("from_gzfile_boxed: nr="s + to_string(nr) + " errno="s + gzerror(fp, &errnum));
  }
  if (nr == 0) return packet(); // EOF
  if (nr != sizeof(todo)) throw runtime_error("from_gzfile_boxed: short read 1");

  packet ret(todo + 32);
  nr = gzread(fp, ret.ptr(), todo);
  if (nr < 0) throw runtime_error("from_gzfile_boxed: read");
  if (U32(nr) != todo) throw runtime_error("from_gzfile_boxed: short read 2");
  ret.wr_pos += nr;

  return ret;
}

packet packet::read_from_fd(int fd)
{
  struct stat st {
  };
  if (fstat(fd, &st) < 0) {
    return packet(0);
  }

  packet ret(st.st_size + 8192);
  ret.add_file_contents(fd);
  close(fd);
  return ret;
}

packet packet::read_from_file(char const *fn)
{
  int fd = open(fn, O_RDONLY, 0);
  if (fd < 0) {
    eprintf("Can't open %s: %s\n", fn, strerror(errno));
    return packet(0);
  }
  packet ret = read_from_fd(fd);
  close(fd);
  return ret;
}

// ----------------------------------------------------------------------

string packet::stats_str()
{
  ostringstream s;
  s << "incref_count=" << stats.incref_count << "\n";
  s << "decref_count=" << stats.decref_count << "\n";
  s << "alloc_count=" << stats.alloc_count << "\n";
  s << "free_count=" << stats.free_count << "\n";
  s << "cow_count=" << stats.cow_count << "\n";
  s << "expand_count=" << stats.expand_count << "\n";
  s << "copy_bytes_count=" << stats.copy_bytes_count << "\n";
  return s.str();
}

void packet::clear_stats()
{
  memset(&stats, 0, sizeof(stats));
}

// ----------------------------------------------------------------------

packet_wr_overrun_err::packet_wr_overrun_err(int _howmuch)
    : overflow_error("Packet wr overrun by "s + to_string(_howmuch)), howmuch(_howmuch)
{
}

packet_wr_overrun_err::~packet_wr_overrun_err()
{
}


packet_rd_overrun_err::packet_rd_overrun_err(int _howmuch)
    : overflow_error(
        _howmuch > 0 ? ("Packet rd overrun by "s + to_string(_howmuch))
                     : ("Packet rd underrun by "s + to_string(-_howmuch))),
      howmuch(_howmuch)
{
}

packet_rd_overrun_err::~packet_rd_overrun_err()
{
}


packet_rd_type_err::packet_rd_type_err(string const &_expected, string const &_got)
    : invalid_argument("Packet rd type error(expected "s + _expected + ", got "s + _got + ")"s),
      expected(_expected),
      got(_got)
{
}

packet_rd_type_err::~packet_rd_type_err()
{
}

// ----------------------------------------------------------------------

namespace packetio {
string packet_get_typetag(S8 const & /* x */)
{
  return "S8";
}
string packet_get_typetag(char const & /* x */)
{
  return "char";
}
string packet_get_typetag(U8 const & /* x */)
{
  return "U8";
}
string packet_get_typetag(S16 const & /* x */)
{
  return "S16";
}
string packet_get_typetag(U16 const & /* x */)
{
  return "U16";
}
string packet_get_typetag(S32 const & /* x */)
{
  return "S32";
}
string packet_get_typetag(U32 const & /* x */)
{
  return "U32";
}
string packet_get_typetag(S64 const & /* x */)
{
  return "S64";
}
string packet_get_typetag(U64 const & /* x */)
{
  return "U64";
}
string packet_get_typetag(float const & /* x */)
{
  return "float";
}
string packet_get_typetag(double const & /* x */)
{
  return "double";
}
string packet_get_typetag(timeval const & /* x */)
{
  return "timeval";
}
string packet_get_typetag(bool const & /* x */)
{
  return "bool";
}


void packet_wr_value(packet &p, S8 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, char const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, U8 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, S16 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, U16 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, S32 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, U32 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, S64 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, U64 const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, float const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, double const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, timeval const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}
void packet_wr_value(packet &p, bool const &x)
{
  p.add_bytes(reinterpret_cast<u_char const *>(&x), sizeof(x));
}

void packet_rd_value(packet &p, S8 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, U8 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, char &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, S16 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, U16 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, S32 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, U32 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, S64 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, U64 &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, float &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, double &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, timeval &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}
void packet_rd_value(packet &p, bool &x)
{
  p.get_bytes(reinterpret_cast<u_char *>(&x), sizeof(x));
}

std::function<void(packet &, S8 &)> packet_rd_value_compat(S8 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, S8 &)>(packet_rd_value);
}
std::function<void(packet &, U8 &)> packet_rd_value_compat(U8 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, U8 &)>(packet_rd_value);
}
std::function<void(packet &, char &)> packet_rd_value_compat(char const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, char &)>(packet_rd_value);
}
std::function<void(packet &, S16 &)> packet_rd_value_compat(S16 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, S16 &)>(packet_rd_value);
}
std::function<void(packet &, U16 &)> packet_rd_value_compat(U16 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, U16 &)>(packet_rd_value);
}
std::function<void(packet &, S32 &)> packet_rd_value_compat(S32 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, S32 &)>(packet_rd_value);
}
std::function<void(packet &, U32 &)> packet_rd_value_compat(U32 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, U32 &)>(packet_rd_value);
}
std::function<void(packet &, S64 &)> packet_rd_value_compat(S64 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, S64 &)>(packet_rd_value);
}
std::function<void(packet &, U64 &)> packet_rd_value_compat(U64 const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, U64 &)>(packet_rd_value);
}
std::function<void(packet &, float &)> packet_rd_value_compat(float const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, float &)>(packet_rd_value);
}
std::function<void(packet &, double &)> packet_rd_value_compat(double const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, double &)>(packet_rd_value);
}
std::function<void(packet &, timeval &)> packet_rd_value_compat(timeval const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, timeval &)>(packet_rd_value);
}
std::function<void(packet &, bool &)> packet_rd_value_compat(bool const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, bool &)>(packet_rd_value);
}

/*
  string
*/
void packet_wr_value(packet &p, string const &x)
{
  auto size = x.size();

  if (size < 0xff) {
    auto smallsize = static_cast<u_char>(size);
    p.add(smallsize);
  }
  else if (size < size_t(0x3fffffff)) {
    u_char smallsize = 0xff;
    p.add(smallsize);
    p.add(static_cast<u_int>(size));
  }
  else {
    abort();
  }

  p.add_bytes(&x[0], size);
}

void packet_wr_value(packet &p, char const * const &x)
{
  if (!x) {
    p.add((u_char)0);
    return;
  }
  auto size = strlen(x);

  if (size < 0xff) {
    auto smallsize = static_cast<u_char>(size);
    p.add(smallsize);
  }
  else if (size < size_t(0x3fffffff)) {
    u_char smallsize = 0xff;
    p.add(smallsize);
    p.add(static_cast<u_int>(size));
  }
  else {
    abort();
  }

  p.add_bytes(x, size);
}

string packet_get_typetag(string const & /* x */)
{
  return "string";
}
void packet_rd_value(packet &p, string &x)
{
  u_char smallsize;
  p.get(smallsize);
  size_t size;
  if (smallsize == 0xff) {
    size = static_cast<size_t >(p.fget< u_int>());
    if (size >= size_t(0x3fffffff)) abort();
  }
  else {
    size = smallsize;
  }
  if (static_cast<ssize_t >(size)> p.remaining()) {
    throw packet_rd_overrun_err(size - p.remaining());
  }
  x.resize(size);
  p.get_bytes(&x[0], size);
}

std::function<void(packet &, string &)> packet_rd_value_compat(string const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, string &)>(packet_rd_value);
}


void packet_rd_value(packet &p, char *&x)
{
  u_char smallsize;
  p.get(smallsize);
  size_t size;
  if (smallsize == 0xff) {
    size = static_cast<size_t >(p.fget< u_int>());
    if (size >= size_t(0x3fffffff)) abort();
  }
  else {
    size = smallsize;
  }
  if (static_cast<ssize_t >(size)> p.remaining()) {
    throw packet_rd_overrun_err(size - p.remaining());
  }
  x = (char *)malloc(size+1);
  p.get_bytes(x, size);
}


/*
  complex<double>
*/

void packet_wr_value(packet &p, std::complex<double> const &x)
{
  packet_wr_value(p, x.real());
  packet_wr_value(p, x.imag());
}
string packet_get_typetag(std::complex<double> const & /* x */)
{
  return "cx_double";
}
void packet_rd_value(packet &p, std::complex<double> &x)
{
  double real, imag;
  packet_rd_value(p, real);
  packet_rd_value(p, imag);
  x = std::complex<double>(real, imag);
}

std::function<void(packet &, std::complex<double> &)>
packet_rd_value_compat(std::complex<double> const &x, string const &typetag)
{
  return static_cast<void (*)(packet &, std::complex<double> &)>(packet_rd_value);
}



template <>
void packet_rd_value(::packet &p, vector<U8> &x)
{
  uint32_t size;
  packetio::packet_rd_value(p, size);
  if (!(size < 0x3fffffff)) throw runtime_error("Unreasonable size "s + to_string(size));
  x.resize(size);
  p.get_bytes(x.data(), size);
}

template <>
void packet_wr_value(::packet &p, vector<U8> const &x)
{
  if (!(x.size() < 0x3fffffff)) throw runtime_error("Unreasonable size "s + to_string(x.size()));
  packetio::packet_wr_value(p, (uint32_t)x.size());
  p.add_bytes(x.data(), x.size());
}



} // namespace packetio

#define INSTANTIATE_PACKET(T) \
  template void packet::add_checked(const T &x); \
  template void packet::add(const T &x); \
  template void packet::add_typetag(const T &x); \
  template void packet::get(T &x); \
  template void packet::get_checked(T &x); \
  template void packet::get_compat(T &x); \
  template std::function<void(packet &, T &)> packet::get_compat_func(T const &x, string const &typetag); \
  template T packet::fget<T>(); \
  template T packet::fget_checked<T>(); \
  template T packet::fget_compat<T>();



INSTANTIATE_PACKET(S8);
INSTANTIATE_PACKET(char);
INSTANTIATE_PACKET(U8);
INSTANTIATE_PACKET(S16);
INSTANTIATE_PACKET(U16);
INSTANTIATE_PACKET(S32);
INSTANTIATE_PACKET(U32);
INSTANTIATE_PACKET(S64);
INSTANTIATE_PACKET(U64);
INSTANTIATE_PACKET(float);
INSTANTIATE_PACKET(double);
INSTANTIATE_PACKET(timeval);
INSTANTIATE_PACKET(bool);
INSTANTIATE_PACKET(string);
INSTANTIATE_PACKET(std::complex<double>);

using mss_t = map<string,string>;
INSTANTIATE_PACKET(mss_t);

INSTANTIATE_PACKET(vector<string>);
INSTANTIATE_PACKET(vector<double>);


// ----------------------------------------------------------------------

string packet::run_test(int testid)
{
  clear_stats();

  if (testid == 0) {
    packet wr;
    wr.add(17);
    wr.add(99);
    packet wr2 = wr;
    return packet::stats_str();
  }
  else {
    throw runtime_error("No such test");
  }
}

//
