Example #1
0
String f_posix_ctermid() {
  String s = String(L_ctermid, ReserveString);
  char *buffer = s.bufferSlice().ptr;
  ctermid(buffer);
  s.setSize(strlen(buffer));
  return s;
}
Example #2
0
String xml_utf8_decode(const XML_Char *s, int len,
                       const XML_Char *encoding) {
  int pos = len;
  String str = String(len, ReserveString);
  char *newbuf = str.bufferSlice().ptr;
  unsigned short c;
  char (*decoder)(unsigned short) = NULL;
  xml_encoding *enc = xml_get_encoding(encoding);

  int newlen = 0;
  if (enc) {
    decoder = enc->decoding_function;
  }
  if (decoder == NULL) {
    /* If the target encoding was unknown, or no decoder function
     * was specified, return the UTF-8-encoded data as-is.
     */
    memcpy(newbuf, s, len);
    str.setSize(len);
    return str;
  }
  while (pos > 0) {
    c = (unsigned char)(*s);
    if (c >= 0xf0) { /* four bytes encoded, 21 bits */
      if (pos-4 >= 0) {
        c = ((s[0]&7)<<18) | ((s[1]&63)<<12) | ((s[2]&63)<<6) | (s[3]&63);
      } else {
        c = '?';
      }
      s += 4;
      pos -= 4;
    } else if (c >= 0xe0) { /* three bytes encoded, 16 bits */
      if (pos-3 >= 0) {
        c = ((s[0]&63)<<12) | ((s[1]&63)<<6) | (s[2]&63);
      } else {
        c = '?';
      }
      s += 3;
      pos -= 3;
    } else if (c >= 0xc0) { /* two bytes encoded, 11 bits */
      if (pos-2 >= 0) {
        c = ((s[0]&63)<<6) | (s[1]&63);
      } else {
        c = '?';
      }
      s += 2;
      pos -= 2;
    } else {
      s++;
      pos--;
    }
    newbuf[newlen] = decoder ? decoder(c) : c;
    ++newlen;
  }

  assert(newlen <= len);
  str.shrink(newlen);
  return str;
}
Example #3
0
String f_posix_getcwd() {
  String s = String(PATH_MAX, ReserveString);
  char *buffer = s.bufferSlice().ptr;
  if (getcwd(buffer, PATH_MAX) == NULL) {
    return "/";
  }
  s.setSize(strlen(buffer));
  return s;
}
Example #4
0
Variant f_qlzuncompress(CStrRef data, int level /* = 1 */) {
#ifndef HAVE_QUICKLZ
  throw NotSupportedException(__func__, "QuickLZ library cannot be found");
#else
  if (level < 1 || level > 3) {
    throw_invalid_argument("level: %d", level);
    return false;
  }

  if (data.size() < 9) {
    raise_notice("passing invalid data to qlzuncompress()");
    return false;
  }

  size_t size = QuickLZ1::qlz_size_decompressed(data.data());
  if ((int64_t)size < 0 ||
      (RuntimeOption::SerializationSizeLimit > 0 &&
       (int64_t)size > RuntimeOption::SerializationSizeLimit)) {
    raise_notice("invalid size in compressed header: %zd", size);
    return false;
  }

  String s = String(size, ReserveString);
  char *decompressed = s.bufferSlice().ptr;
  size_t dsize = 0;

  switch (level) {
    case 1: {
      QuickLZ1::qlz_state_decompress state;
      memset(&state, 0, sizeof(state));
      dsize = QuickLZ1::qlz_decompress(data.data(), decompressed, &state);
      break;
    }
    case 2: {
      QuickLZ2::qlz_state_decompress state;
      memset(&state, 0, sizeof(state));
      dsize = QuickLZ2::qlz_decompress(data.data(), decompressed, &state);
      break;
    }
    case 3: {
      QuickLZ3::qlz_state_decompress *state =
        new QuickLZ3::qlz_state_decompress();
      memset(state, 0, sizeof(*state));
      dsize = QuickLZ3::qlz_decompress(data.data(), decompressed, state);
      delete state;
      break;
    }
  }

  assert(dsize == size);
  return s.setSize(dsize);
#endif
}
Example #5
0
static String oidAsString(MongoDBBsonObjectIDData* data)
{
	String s;
	char *data_s;

	s = String(24, ReserveString);
	data_s = s.bufferSlice().data();
	bson_oid_to_string(&data->m_oid, data_s);
	s.setSize(24);

	return s;
}
Example #6
0
Variant HHVM_FUNCTION(snuncompress, const String& data) {
  size_t dsize;

  snappy::GetUncompressedLength(data.data(), data.size(), &dsize);
  String s = String(dsize, ReserveString);
  char *uncompressed = s.bufferSlice().ptr;

  if (!snappy::RawUncompress(data.data(), data.size(), uncompressed)) {
    return false;
  }
  return s.setSize(dsize);
}
Example #7
0
File: ext_xml.cpp Project: 2bj/hhvm
String f_utf8_encode(const String& data) {
  String str = String(data.size() * 4, ReserveString);
  char *newbuf = str.bufferSlice().ptr;
  int newlen = 0;
  const char *s = data.data();
  for (int pos = data.size(); pos > 0; pos--, s++) {
    unsigned int c = (unsigned char)(*s);
    if (c < 0x80) {
      newbuf[newlen++] = (char) c;
    } else if (c < 0x800) {
      newbuf[newlen++] = (0xc0 | (c >> 6));
      newbuf[newlen++] = (0x80 | (c & 0x3f));
    } else if (c < 0x10000) {
Example #8
0
static Variant mcrypt_generic(const Resource& td, const String& data,
                              bool dencrypt) {
  MCrypt *pm = td.getTyped<MCrypt>();
  if (!pm->m_init) {
    raise_warning("Operation disallowed prior to mcrypt_generic_init().");
    return false;
  }

  if (data.empty()) {
    raise_warning("An empty string was passed");
    return false;
  }

  String s;
  unsigned char* data_s;
  int block_size, data_size;
  /* Check blocksize */
  if (mcrypt_enc_is_block_mode(pm->m_td) == 1) { /* It's a block algorithm */
    block_size = mcrypt_enc_get_block_size(pm->m_td);
    data_size = (((data.size() - 1) / block_size) + 1) * block_size;
    s = String(data_size, ReserveString);
    data_s = (unsigned char *)s.bufferSlice().ptr;
    memset(data_s, 0, data_size);
    memcpy(data_s, data.data(), data.size());
  } else { /* It's not a block algorithm */
    data_size = data.size();
    s = String(data_size, ReserveString);
    data_s = (unsigned char *)s.bufferSlice().ptr;
    memcpy(data_s, data.data(), data.size());
  }

  if (dencrypt) {
    mdecrypt_generic(pm->m_td, data_s, data_size);
  } else {
    mcrypt_generic(pm->m_td, data_s, data_size);
  }
  s.setSize(data_size);
  return s;
}
Example #9
0
Variant HHVM_FUNCTION(qlzuncompress, const String& data, int level /* = 1 */) {
  if (level < 1 || level > 3) {
    throw_invalid_argument("level: %d", level);
    return false;
  }

  if (data.size() < 9) {
    raise_notice("passing invalid data to qlzuncompress()");
    return false;
  }

  size_t size = QuickLZ1::qlz_size_decompressed(data.data());
  if ((int64_t)size < 0 ||
      (RuntimeOption::SerializationSizeLimit > 0 &&
       (int64_t)size > RuntimeOption::SerializationSizeLimit)) {
    raise_notice("invalid size in compressed header: %zd", size);
    return false;
  }

  String s = String(size, ReserveString);
  char *decompressed = s.bufferSlice().ptr;
  size_t dsize = 0;

  switch (level) {
    case 1: {
      QuickLZ1::qlz_state_decompress state;
      memset(&state, 0, sizeof(state));
      dsize = QuickLZ1::qlz_decompress(data.data(), decompressed, &state);
      break;
    }
    case 2: {
      QuickLZ2::qlz_state_decompress state;
      memset(&state, 0, sizeof(state));
      dsize = QuickLZ2::qlz_decompress(data.data(), decompressed, &state);
      break;
    }
    case 3: {
      QuickLZ3::qlz_state_decompress *state =
        new QuickLZ3::qlz_state_decompress();
      memset(state, 0, sizeof(*state));
      dsize = QuickLZ3::qlz_decompress(data.data(), decompressed, state);
      delete state;
      break;
    }
  }

  if (dsize != size) {
    return false;
  }
  return s.setSize(dsize);
}
Example #10
0
String File::read(int64_t length) {
    if (length <= 0) {
        raise_notice("Invalid length %" PRId64, length);
        // XXX: Changing this to empty_string causes problems, something is
        // writing to this upstream but I'm not sure what and since it's
        // unlikely to provide significant gain alone I'm leaving it for now.
        return "";
    }

    auto const allocSize = length;
    String s = String(allocSize, ReserveString);
    char *ret = s.bufferSlice().ptr;
    int64_t copied = 0;
    int64_t avail = bufferedLen();

    while (avail < length && !eof()) {
        if (m_buffer == nullptr) {
            m_buffer = (char *)malloc(CHUNK_SIZE);
            m_bufferSize = CHUNK_SIZE;
        }

        if (avail > 0) {
            memcpy(ret + copied, m_buffer + m_readpos, avail);
            copied += avail;
            length -= avail;
        }

        m_writepos = filteredReadToBuffer();
        m_readpos = 0;
        avail = bufferedLen();

        if (avail == 0 || m_nonblocking) {
            // For nonblocking mode, temporary out of data.
            break;
        }
    }

    avail = bufferedLen();
    if (avail > 0) {
        int64_t n = length < avail ? length : avail;
        memcpy(ret + copied, m_buffer + m_readpos, n);
        m_readpos += n;
        copied += n;
    }

    m_position += copied;

    assert(copied <= allocSize);
    s.shrink(copied);
    return s;
}
Example #11
0
Variant f_snuncompress(CStrRef data) {
#ifndef HAVE_SNAPPY
  throw NotSupportedException(__func__, "Snappy library cannot be found");
#else
  size_t dsize;

  snappy::GetUncompressedLength(data.data(), data.size(), &dsize);
  String s = String(dsize, ReserveString);
  char *uncompressed = s.bufferSlice().ptr;

  if (!snappy::RawUncompress(data.data(), data.size(), uncompressed)) {
    return false;
  }
  return s.setSize(dsize);
#endif
}
Example #12
0
Variant HHVM_FUNCTION(lz4uncompress, const String& compressed) {
  const char* compressed_ptr = compressed.data();
  int dsize = VarintDecode(&compressed_ptr, compressed.size());
  if (dsize < 0) {
    return false;
  }

  String s = String(dsize, ReserveString);
  char *uncompressed = s.bufferSlice().ptr;
  int ret = LZ4_uncompress(compressed_ptr, uncompressed, dsize);

  if (ret <= 0) {
    return false;
  }
  return s.setSize(dsize);
}
Example #13
0
Variant HHVM_FUNCTION(lz4hccompress, const String& uncompressed) {
  int bufsize = LZ4_compressBound(uncompressed.size());
  if (bufsize < 0) {
    return false;
  }
  int headerSize = VarintSize(uncompressed.size());
  bufsize += headerSize;  // for the header
  String s = String(bufsize, ReserveString);
  char *compressed = s.bufferSlice().ptr;

  VarintEncode(uncompressed.size(), &compressed);  // write the header

  int csize = LZ4_compressHC(uncompressed.data(),
      compressed, uncompressed.size());
  if (csize < 0) {
    return false;
  }
  bufsize = csize + headerSize;
  return s.shrink(bufsize);
}
Example #14
0
String StringUtil::Implode(const Variant& items, const String& delim) {
  if (!isContainer(items)) {
    throw_param_is_not_container();
  }
  int size = getContainerSize(items);
  if (size == 0) return "";

  String* sitems = (String*)smart_malloc(size * sizeof(String));
  int len = 0;
  int lenDelim = delim.size();
  int i = 0;
  for (ArrayIter iter(items); iter; ++iter) {
    new (&sitems[i]) String(iter.second().toString());
    len += sitems[i].size() + lenDelim;
    i++;
  }
  len -= lenDelim; // always one delimiter less than count of items
  assert(i == size);

  String s = String(len, ReserveString);
  char *buffer = s.bufferSlice().ptr;
  const char *sdelim = delim.data();
  char *p = buffer;
  for (int i = 0; i < size; i++) {
    String &item = sitems[i];
    if (i && lenDelim) {
      memcpy(p, sdelim, lenDelim);
      p += lenDelim;
    }
    int lenItem = item.size();
    if (lenItem) {
      memcpy(p, item.data(), lenItem);
      p += lenItem;
    }
    sitems[i].~String();
  }
  smart_free(sitems);
  assert(p - buffer == len);
  s.setSize(len);
  return s;
}
Example #15
0
Variant f_lz4compress(CStrRef uncompressed) {
  int bufsize = LZ4_compressBound(uncompressed.size());
  if (bufsize < 0) {
    return false;
  }
  int headerSize = VarintSize(uncompressed.size());
  bufsize += headerSize;  // for the header
  String s = String(bufsize, ReserveString);
  char *compressed = s.bufferSlice().ptr;

  VarintEncode(uncompressed.size(), &compressed);  // write the header

  int csize = LZ4_compress(uncompressed.data(), compressed,
      uncompressed.size());
  if (csize < 0) {
    return false;
  }
  bufsize = csize + headerSize;
  s.shrink(bufsize);
  return s.setSize(bufsize);
}
Example #16
0
bool FileRepository::readActualFile(const StringData *name,
                                    const struct stat &s,
                                    FileInfo &fileInfo) {
  if (s.st_size > StringData::MaxSize) {
    throw FatalErrorException(0, "file %s is too big", name->data());
  }
  int fileSize = s.st_size;
  int fd = open(name->data(), O_RDONLY);
  if (!fd) return false; // ignore file open exception
  String str = String(fileSize, ReserveString);
  char *input = str.bufferSlice().ptr;
  if (!input) return false;
  int nbytes = read(fd, input, fileSize);
  close(fd);
  str.setSize(fileSize);
  fileInfo.m_inputString = str;
  if (nbytes != fileSize) return false;

  computeMd5(name, fileInfo);
  return true;
}
Example #17
0
Variant f_stream_get_contents(CResRef handle, int maxlen /* = -1 */,
                              int offset /* = -1 */) {
  if (maxlen < -1) {
    throw_invalid_argument("maxlen: %d", maxlen);
    return false;
  }

  if (maxlen == 0) {
    return String();
  }

  File *file = handle.getTyped<File>(false /* nullOkay */,
                                     true /* badTypeOkay */);
  if (!file) {
    throw_invalid_argument(
      "stream_get_contents() expects parameter 1 to be a resource");
    return false;
  }

  if (offset >= 0 && !file->seek(offset, SEEK_SET) ) {
    raise_warning("Failed to seek to position %d in the stream", offset);
    return false;
  }

  String ret;
  if (maxlen != -1) {
    ret = String(maxlen, ReserveString);
    char *buf = ret.bufferSlice().ptr;
    maxlen = file->readImpl(buf, maxlen);
    if (maxlen < 0) {
      return false;
    }
    ret.setSize(maxlen);
  } else {
    StringBuffer sb;
    sb.read(file);
    ret = sb.detach();
  }
  return ret;
}
Example #18
0
File: ext_xml.cpp Project: 2bj/hhvm
String f_utf8_decode(const String& data) {
  String str = String(data.size(), ReserveString);
  char *newbuf = str.bufferSlice().ptr;
  int newlen = 0;
  const char *s = data.data();
  for (int pos = data.size(); pos > 0; ) {
    unsigned short c = (unsigned char)(*s);
    if (c >= 0xf0) { /* four bytes encoded, 21 bits */
      if (pos-4 >= 0) {
        c = ((s[0]&7)<<18) | ((s[1]&63)<<12) | ((s[2]&63)<<6) | (s[3]&63);
      } else {
        c = '?';
      }
      s += 4;
      pos -= 4;
    } else if (c >= 0xe0) { /* three bytes encoded, 16 bits */
      if (pos-3 >= 0) {
        c = ((s[0]&63)<<12) | ((s[1]&63)<<6) | (s[2]&63);
      } else {
        c = '?';
      }
      s += 3;
      pos -= 3;
    } else if (c >= 0xc0) { /* two bytes encoded, 11 bits */
      if (pos-2 >= 0) {
        c = ((s[0]&63)<<6) | (s[1]&63);
      } else {
        c = '?';
      }
      s += 2;
      pos -= 2;
    } else {
      s++;
      pos--;
    }
    newbuf[newlen] = (char)(c > 0xff ? '?' : c);
    ++newlen;
  }
  return str.setSize(newlen);
}
Example #19
0
static unsigned char *php_parserr(unsigned char *cp, unsigned char* end,
                                  querybuf *answer,
                                  int type_to_fetch, bool store,
                                  Array &subarray) {
    unsigned short type, cls ATTRIBUTE_UNUSED, dlen;
    unsigned long ttl;
    int64_t n, i;
    unsigned short s;
    unsigned char *tp, *p;
    char name[MAXHOSTNAMELEN];
    int have_v6_break = 0, in_v6_break = 0;

    n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2);
    if (n < 0) {
        return NULL;
    }
    cp += n;

    CHECKCP(10);
    GETSHORT(type, cp);
    GETSHORT(cls, cp);
    GETLONG(ttl, cp);
    GETSHORT(dlen, cp);
    CHECKCP(dlen);
    if (type_to_fetch != T_ANY && type != type_to_fetch) {
        cp += dlen;
        return cp;
    }

    if (!store) {
        cp += dlen;
        return cp;
    }

    subarray.set(s_host, String(name, CopyString));
    switch (type) {
    case DNS_T_A:
        CHECKCP(4);
        subarray.set(s_type, s_A);
        snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
        subarray.set(s_ip, String(name, CopyString));
        cp += dlen;
        break;
    case DNS_T_MX:
        CHECKCP(2);
        subarray.set(s_type, s_MX);
        GETSHORT(n, cp);
        subarray.set(s_pri, n);
    /* no break; */
    case DNS_T_CNAME:
        if (type == DNS_T_CNAME) {
            subarray.set(s_type, s_CNAME);
        }
    /* no break; */
    case DNS_T_NS:
        if (type == DNS_T_NS) {
            subarray.set(s_type, s_NS);
        }
    /* no break; */
    case DNS_T_PTR:
        if (type == DNS_T_PTR) {
            subarray.set(s_type, s_PTR);
        }
        n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
        if (n < 0) {
            return NULL;
        }
        cp += n;
        subarray.set(s_target, String(name, CopyString));
        break;
    case DNS_T_HINFO:
        /* See RFC 1010 for values */
        subarray.set(s_type, s_HINFO);
        CHECKCP(1);
        n = *cp & 0xFF;
        cp++;
        CHECKCP(n);
        subarray.set(s_cpu, String((const char *)cp, n, CopyString));
        cp += n;
        CHECKCP(1);
        n = *cp & 0xFF;
        cp++;
        CHECKCP(n);
        subarray.set(s_os, String((const char *)cp, n, CopyString));
        cp += n;
        break;
    case DNS_T_TXT: {
        int l1 = 0, l2 = 0;

        String s = String(dlen, ReserveString);
        tp = (unsigned char *)s.bufferSlice().ptr;

        while (l1 < dlen) {
            n = cp[l1];
            if ((n + l1) > dlen) {
                // bad record, don't set anything
                break;
            }
            memcpy(tp + l1 , cp + l1 + 1, n);
            l1 = l1 + n + 1;
            l2 = l2 + n;
        }
        s.setSize(l2);
        cp += dlen;

        subarray.set(s_type, s_TXT);
        subarray.set(s_txt, s);
        break;
    }
    case DNS_T_SOA:
        subarray.set(s_type, s_SOA);
        n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
        if (n < 0) {
            return NULL;
        }
        cp += n;
        subarray.set(s_mname, String(name, CopyString));
        n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
        if (n < 0) {
            return NULL;
        }
        cp += n;
        subarray.set(s_rname, String(name, CopyString));
        CHECKCP(5*4);
        GETLONG(n, cp);
        subarray.set(s_serial, n);
        GETLONG(n, cp);
        subarray.set(s_refresh, n);
        GETLONG(n, cp);
        subarray.set(s_retry, n);
        GETLONG(n, cp);
        subarray.set(s_expire, n);
        GETLONG(n, cp);
        subarray.set(s_minimum_ttl, n);
        break;
    case DNS_T_AAAA:
        tp = (unsigned char *)name;
        CHECKCP(8*2);
        for (i = 0; i < 8; i++) {
            GETSHORT(s, cp);
            if (s != 0) {
                if (tp > (u_char *)name) {
                    in_v6_break = 0;
                    tp[0] = ':';
                    tp++;
                }
                tp += sprintf((char *)tp, "%x", s);
            } else {
                if (!have_v6_break) {
                    have_v6_break = 1;
                    in_v6_break = 1;
                    tp[0] = ':';
                    tp++;
                } else if (!in_v6_break) {
                    tp[0] = ':';
                    tp++;
                    tp[0] = '0';
                    tp++;
                }
            }
        }
        if (have_v6_break && in_v6_break) {
            tp[0] = ':';
            tp++;
        }
        tp[0] = '\0';
        subarray.set(s_type, s_AAAA);
        subarray.set(s_ipv6, String(name, CopyString));
        break;
    case DNS_T_A6:
        p = cp;
        subarray.set(s_type, s_A6);
        CHECKCP(1);
        n = ((int)cp[0]) & 0xFF;
        cp++;
        subarray.set(s_masklen, n);
        tp = (unsigned char *)name;
        if (n > 15) {
            have_v6_break = 1;
            in_v6_break = 1;
            tp[0] = ':';
            tp++;
        }
        if (n % 16 > 8) {
            /* Partial short */
            if (cp[0] != 0) {
                if (tp > (u_char *)name) {
                    in_v6_break = 0;
                    tp[0] = ':';
                    tp++;
                }
                sprintf((char *)tp, "%x", cp[0] & 0xFF);
            } else {
                if (!have_v6_break) {
                    have_v6_break = 1;
                    in_v6_break = 1;
                    tp[0] = ':';
                    tp++;
                } else if (!in_v6_break) {
                    tp[0] = ':';
                    tp++;
                    tp[0] = '0';
                    tp++;
                }
            }
            cp++;
        }
        for (i = (n + 8)/16; i < 8; i++) {
            CHECKCP(2);
            GETSHORT(s, cp);
            if (s != 0) {
                if (tp > (u_char *)name) {
                    in_v6_break = 0;
                    tp[0] = ':';
                    tp++;
                }
                tp += sprintf((char*)tp,"%x",s);
            } else {
                if (!have_v6_break) {
                    have_v6_break = 1;
                    in_v6_break = 1;
                    tp[0] = ':';
                    tp++;
                } else if (!in_v6_break) {
                    tp[0] = ':';
                    tp++;
                    tp[0] = '0';
                    tp++;
                }
            }
        }
        if (have_v6_break && in_v6_break) {
            tp[0] = ':';
            tp++;
        }
        tp[0] = '\0';
        subarray.set(s_ipv6, String(name, CopyString));
        if (cp < p + dlen) {
            n = dn_expand(answer->qb2, end, cp, name,
                          (sizeof name) - 2);
            if (n < 0) {
                return NULL;
            }
            cp += n;
            subarray.set(s_chain, String(name, CopyString));
        }
        break;
    case DNS_T_SRV:
        CHECKCP(3*2);
        subarray.set(s_type, s_SRV);
        GETSHORT(n, cp);
        subarray.set(s_pri, n);
        GETSHORT(n, cp);
        subarray.set(s_weight, n);
        GETSHORT(n, cp);
        subarray.set(s_port, n);
        n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
        if (n < 0) {
            return NULL;
        }
        cp += n;
        subarray.set(s_target, String(name, CopyString));
        break;
    case DNS_T_NAPTR:
        CHECKCP(2*2);
        subarray.set(s_type, s_NAPTR);
        GETSHORT(n, cp);
        subarray.set(s_order, n);
        GETSHORT(n, cp);
        subarray.set(s_pref, n);

        CHECKCP(1);
        n = (cp[0] & 0xFF);
        ++cp;
        CHECKCP(n);
        subarray.set(s_flags, String((const char *)cp, n, CopyString));
        cp += n;

        CHECKCP(1);
        n = (cp[0] & 0xFF);
        ++cp;
        CHECKCP(n);
        subarray.set(s_services, String((const char *)cp, n, CopyString));
        cp += n;

        CHECKCP(1);
        n = (cp[0] & 0xFF);
        ++cp;
        CHECKCP(n);
        subarray.set(s_regex, String((const char *)cp, n, CopyString));
        cp += n;

        n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
        if (n < 0) {
            return NULL;
        }
        cp += n;
        subarray.set(s_replacement, String(name, CopyString));
        break;
    default:
        cp += dlen;
    }

    subarray.set(s_class, s_IN);
    subarray.set(s_ttl, (int)ttl);
    return cp;
}
Example #20
0
Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport,
                           CArrRef fieldspec) {
  Variant ret;
  switch (thrift_typeID) {
    case T_STOP:
    case T_VOID:
      return uninit_null();
    case T_STRUCT: {
      Variant val;
      if ((val = fieldspec.rvalAt(PHPTransport::s_class)).isNull()) {
        throw_tprotocolexception("no class type in spec", INVALID_DATA);
        skip_element(T_STRUCT, transport);
        return uninit_null();
      }
      String structType = val.toString();
      ret = createObject(structType);
      if (ret.isNull()) {
        // unable to create class entry
        skip_element(T_STRUCT, transport);
        return uninit_null();
      }
      Variant spec = f_hphp_get_static_property(structType, s_TSPEC, false);
      if (!spec.is(KindOfArray)) {
        char errbuf[128];
        snprintf(errbuf, 128, "spec for %s is wrong type: %d\n",
                 structType.data(), ret.getType());
        throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA);
        return uninit_null();
      }
      binary_deserialize_spec(ret.toObject(), transport, spec.toArray());
      return ret;
    } break;
    case T_BOOL: {
      uint8_t c;
      transport.readBytes(&c, 1);
      return c != 0;
    }
  //case T_I08: // same numeric value as T_BYTE
    case T_BYTE: {
      uint8_t c;
      transport.readBytes(&c, 1);
      return Variant((int8_t)c);
    }
    case T_I16: {
      uint16_t c;
      transport.readBytes(&c, 2);
      return Variant((int16_t)ntohs(c));
    }
    case T_I32: {
      uint32_t c;
      transport.readBytes(&c, 4);
      return Variant((int32_t)ntohl(c));
    }
    case T_U64:
    case T_I64: {
      uint64_t c;
      transport.readBytes(&c, 8);
      return Variant((int64_t)ntohll(c));
    }
    case T_DOUBLE: {
      union {
        uint64_t c;
        double d;
      } a;
      transport.readBytes(&(a.c), 8);
      a.c = ntohll(a.c);
      return a.d;
    }
    case T_FLOAT: {
      union {
        uint32_t c;
        float d;
      } a;
      transport.readBytes(&(a.c), 4);
      a.c = ntohl(a.c);
      return a.d;
    }
    //case T_UTF7: // aliases T_STRING
    case T_UTF8:
    case T_UTF16:
    case T_STRING: {
      uint32_t size = transport.readU32();
      if (size && (size + 1)) {
        String s = String(size, ReserveString);
        char* strbuf = s.bufferSlice().ptr;
        transport.readBytes(strbuf, size);
        return s.setSize(size);
      } else {
        return "";
      }
    }
    case T_MAP: { // array of key -> value
      uint8_t types[2];
      transport.readBytes(types, 2);
      uint32_t size = transport.readU32();

      Array keyspec = fieldspec.rvalAt(PHPTransport::s_key,
                                       AccessFlags::Error_Key).toArray();
      Array valspec = fieldspec.rvalAt(PHPTransport::s_val,
                                       AccessFlags::Error_Key).toArray();
      ret = Array::Create();

      for (uint32_t s = 0; s < size; ++s) {
        Variant key = binary_deserialize(types[0], transport, keyspec);
        Variant value = binary_deserialize(types[1], transport, valspec);
        ret.set(key, value);
      }
      return ret; // return_value already populated
    }
    case T_LIST: { // array with autogenerated numeric keys
      int8_t type = transport.readI8();
      uint32_t size = transport.readU32();
      Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem,
                                         AccessFlags::Error_Key);
      Array elemspec = elemvar.toArray();
      ret = Array::Create();

      for (uint32_t s = 0; s < size; ++s) {
        Variant value = binary_deserialize(type, transport, elemspec);
        ret.append(value);
      }
      return ret;
    }
    case T_SET: { // array of key -> TRUE
      uint8_t type;
      uint32_t size;
      transport.readBytes(&type, 1);
      transport.readBytes(&size, 4);
      size = ntohl(size);
      Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem,
                                         AccessFlags::Error_Key);
      Array elemspec = elemvar.toArray();
      ret = Array::Create();

      for (uint32_t s = 0; s < size; ++s) {
        Variant key = binary_deserialize(type, transport, elemspec);

        if (key.isInteger()) {
          ret.set(key, true);
        } else {
          ret.set(key.toString(), true);
        }
      }
      return ret;
    }
  };

  char errbuf[128];
  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
  throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA);
  return uninit_null();
}
Example #21
0
static Variant php_mcrypt_do_crypt(const String& cipher, const String& key, const String& data,
                                   const String& mode, const String& iv, bool dencrypt) {
    MCRYPT td = mcrypt_module_open((char*)cipher.data(),
                                   (char*)MCG(algorithms_dir).data(),
                                   (char*)mode.data(),
                                   (char*)MCG(modes_dir).data());
    if (td == MCRYPT_FAILED) {
        raise_warning(MCRYPT_OPEN_MODULE_FAILED);
        return false;
    }

    /* Checking for key-length */
    int max_key_length = mcrypt_enc_get_key_size(td);
    if (key.size() > max_key_length) {
        raise_warning("Size of key is too large for this algorithm");
    }
    int count;
    int *key_length_sizes = mcrypt_enc_get_supported_key_sizes(td, &count);
    int use_key_length;
    char *key_s = NULL;
    if (count == 0 && key_length_sizes == NULL) { // all lengths 1 - k_l_s = OK
        use_key_length = key.size();
        key_s = (char*)malloc(use_key_length);
        memcpy(key_s, key.data(), use_key_length);
    } else if (count == 1) {  /* only m_k_l = OK */
        key_s = (char*)malloc(key_length_sizes[0]);
        memset(key_s, 0, key_length_sizes[0]);
        memcpy(key_s, key.data(), MIN(key.size(), key_length_sizes[0]));
        use_key_length = key_length_sizes[0];
    } else { /* dertermine smallest supported key > length of requested key */
        use_key_length = max_key_length; /* start with max key length */
        for (int i = 0; i < count; i++) {
            if (key_length_sizes[i] >= key.size() &&
                    key_length_sizes[i] < use_key_length) {
                use_key_length = key_length_sizes[i];
            }
        }
        key_s = (char*)malloc(use_key_length);
        memset(key_s, 0, use_key_length);
        memcpy(key_s, key.data(), MIN(key.size(), use_key_length));
    }
    mcrypt_free(key_length_sizes);

    /* Check IV */
    char *iv_s = NULL;
    int iv_size = mcrypt_enc_get_iv_size(td);

    /* IV is required */
    if (mcrypt_enc_mode_has_iv(td) == 1) {
        if (!iv.empty()) {
            if (iv_size != iv.size()) {
                raise_warning("The IV parameter must be as long as the blocksize");
            } else {
                iv_s = (char*)malloc(iv_size + 1);
                memcpy(iv_s, iv.data(), iv_size);
            }
        } else {
            raise_warning("Attempt to use an empty IV, which is NOT recommended");
            iv_s = (char*)malloc(iv_size + 1);
            memset(iv_s, 0, iv_size + 1);
        }
    }

    int block_size;
    unsigned long int data_size;
    String s;
    char *data_s;
    /* Check blocksize */
    if (mcrypt_enc_is_block_mode(td) == 1) { /* It's a block algorithm */
        block_size = mcrypt_enc_get_block_size(td);
        data_size = (((data.size() - 1) / block_size) + 1) * block_size;
        s = String(data_size, ReserveString);
        data_s = (char*)s.bufferSlice().ptr;
        memset(data_s, 0, data_size);
        memcpy(data_s, data.data(), data.size());
    } else { /* It's not a block algorithm */
        data_size = data.size();
        s = String(data_size, ReserveString);
        data_s = (char*)s.bufferSlice().ptr;
        memcpy(data_s, data.data(), data.size());
    }

    if (mcrypt_generic_init(td, key_s, use_key_length, iv_s) < 0) {
        raise_warning("Mcrypt initialisation failed");
        return false;
    }
    if (dencrypt) {
        mdecrypt_generic(td, data_s, data_size);
    } else {
        mcrypt_generic(td, data_s, data_size);
    }

    /* freeing vars */
    mcrypt_generic_end(td);
    if (key_s != NULL) {
        free(key_s);
    }
    if (iv_s != NULL) {
        free(iv_s);
    }
    return s.setSize(data_size);
}
Example #22
0
Variant ZendPack::pack(const String& fmt, CArrRef argv) {
  /* Preprocess format into formatcodes and formatargs */
  std::vector<char> formatcodes;
  std::vector<int> formatargs;
  int argc = argv.size();

  const char *format = fmt.c_str();
  int formatlen = fmt.size();
  int currentarg = 0;
  for (int i = 0; i < formatlen; ) {
    char code = format[i++];
    int arg = 1;

    /* Handle format arguments if any */
    if (i < formatlen) {
      char c = format[i];

      if (c == '*') {
        arg = -1;
        i++;
      }
      else if (c >= '0' && c <= '9') {
        arg = atoi(&format[i]);

        while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
          i++;
        }
      }
    }

    /* Handle special arg '*' for all codes and check argv overflows */
    switch ((int) code) {
      /* Never uses any args */
    case 'x':
    case 'X':
    case '@':
      if (arg < 0) {
        throw_invalid_argument("Type %c: '*' ignored", code);
        arg = 1;
      }
      break;

      /* Always uses one arg */
    case 'a':
    case 'A':
    case 'h':
    case 'H':
      if (currentarg >= argc) {
        throw_invalid_argument("Type %c: not enough arguments", code);
        return false;
      }

      if (arg < 0) {
        arg = argv[currentarg].toString().size();
      }

      currentarg++;
      break;

      /* Use as many args as specified */
    case 'c':
    case 'C':
    case 's':
    case 'S':
    case 'i':
    case 'I':
    case 'l':
    case 'L':
    case 'n':
    case 'N':
    case 'v':
    case 'V':
    case 'f':
    case 'd':
      if (arg < 0) {
        arg = argc - currentarg;
      }

      currentarg += arg;

      if (currentarg > argc) {
        throw_invalid_argument("Type %c: too few arguments", code);
        return false;
      }
      break;

    default:
      throw_invalid_argument("Type %c: unknown format code", code);
      return false;
    }

    formatcodes.push_back(code);
    formatargs.push_back(arg);
  }

  if (currentarg < argc) {
    throw_invalid_argument("%d arguments unused", (argc - currentarg));
  }

  int outputpos = 0, outputsize = 0;
  /* Calculate output length and upper bound while processing*/
  for (int i = 0; i < (int)formatcodes.size(); i++) {
    int code = (int) formatcodes[i];
    int arg = formatargs[i];

    switch ((int) code) {
    case 'h':
    case 'H':
      INC_OUTPUTPOS((arg + (arg % 2)) / 2,1);  /* 4 bit per arg */
      break;

    case 'a':
    case 'A':
    case 'c':
    case 'C':
    case 'x':
      INC_OUTPUTPOS(arg,1);    /* 8 bit per arg */
      break;

    case 's':
    case 'S':
    case 'n':
    case 'v':
      INC_OUTPUTPOS(arg,2);    /* 16 bit per arg */
      break;

    case 'i':
    case 'I':
      INC_OUTPUTPOS(arg,sizeof(int));
      break;

    case 'l':
    case 'L':
    case 'N':
    case 'V':
      INC_OUTPUTPOS(arg,4);    /* 32 bit per arg */
      break;

    case 'f':
      INC_OUTPUTPOS(arg,sizeof(float));
      break;

    case 'd':
      INC_OUTPUTPOS(arg,sizeof(double));
      break;

    case 'X':
      outputpos -= arg;

      if (outputpos < 0) {
        throw_invalid_argument("Type %c: outside of string", code);
        outputpos = 0;
      }
      break;

    case '@':
      outputpos = arg;
      break;
    }

    if (outputsize < outputpos) {
      outputsize = outputpos;
    }
  }

  String s = String(outputsize, ReserveString);
  char *output = s.bufferSlice().ptr;
  outputpos = 0;
  currentarg = 0;

  /* Do actual packing */
  for (int i = 0; i < (int)formatcodes.size(); i++) {
    int code = (int) formatcodes[i];
    int arg = formatargs[i];
    String val;
    const char *s;
    int slen;

    switch ((int) code) {
    case 'a':
    case 'A':
      memset(&output[outputpos], (code == 'a') ? '\0' : ' ', arg);
      val = argv[currentarg++].toString();
      s = val.c_str();
      slen = val.size();
      memcpy(&output[outputpos], s, (slen < arg) ? slen : arg);
      outputpos += arg;
      break;

    case 'h':
    case 'H': {
      int nibbleshift = (code == 'h') ? 0 : 4;
      int first = 1;
      const char *v;

      val = argv[currentarg++].toString();
      v = val.data();
      slen = val.size();
      outputpos--;
      if(arg > slen) {
        throw_invalid_argument
          ("Type %c: not enough characters in string", code);
        arg = slen;
      }

      while (arg-- > 0) {
        char n = *v++;

        if (n >= '0' && n <= '9') {
          n -= '0';
        } else if (n >= 'A' && n <= 'F') {
          n -= ('A' - 10);
        } else if (n >= 'a' && n <= 'f') {
          n -= ('a' - 10);
        } else {
          throw_invalid_argument("Type %c: illegal hex digit %c", code, n);
          n = 0;
        }

        if (first--) {
          output[++outputpos] = 0;
        } else {
          first = 1;
        }

        output[outputpos] |= (n << nibbleshift);
        nibbleshift = (nibbleshift + 4) & 7;
      }

      outputpos++;
      break;
    }

    case 'c':
    case 'C':
      while (arg-- > 0) {
        pack(argv[currentarg++], 1, byte_map, &output[outputpos]);
        outputpos++;
      }
      break;

    case 's':
    case 'S':
    case 'n':
    case 'v': {
      int *map = machine_endian_short_map;

      if (code == 'n') {
        map = big_endian_short_map;
      } else if (code == 'v') {
        map = little_endian_short_map;
      }

      while (arg-- > 0) {
        pack(argv[currentarg++], 2, map, &output[outputpos]);
        outputpos += 2;
      }
      break;
    }

    case 'i':
    case 'I':
      while (arg-- > 0) {
        pack(argv[currentarg++], sizeof(int), int_map, &output[outputpos]);
        outputpos += sizeof(int);
      }
      break;

    case 'l':
    case 'L':
    case 'N':
    case 'V': {
      int *map = machine_endian_int32_map;

      if (code == 'N') {
        map = big_endian_int32_map;
      } else if (code == 'V') {
        map = little_endian_int32_map;
      }

      while (arg-- > 0) {
        pack(argv[currentarg++], 4, map, &output[outputpos]);
        outputpos += 4;
      }
      break;
    }

    case 'f': {
      float v;

      while (arg-- > 0) {
        v = argv[currentarg++].toDouble();
        memcpy(&output[outputpos], &v, sizeof(v));
        outputpos += sizeof(v);
      }
      break;
    }

    case 'd': {
      double v;

      while (arg-- > 0) {
        v = argv[currentarg++].toDouble();
        memcpy(&output[outputpos], &v, sizeof(v));
        outputpos += sizeof(v);
      }
      break;
    }

    case 'x':
      memset(&output[outputpos], '\0', arg);
      outputpos += arg;
      break;

    case 'X':
      outputpos -= arg;

      if (outputpos < 0) {
        outputpos = 0;
      }
      break;

    case '@':
      if (arg > outputpos) {
        memset(&output[outputpos], '\0', arg - outputpos);
      }
      outputpos = arg;
      break;
    }
  }

  return s.setSize(outputpos);
}
Example #23
0
Variant ZendPack::unpack(const String& fmt, const String& data) {
  const char *format = fmt.c_str();
  int formatlen = fmt.size();
  const char *input = data.c_str();
  int inputlen = data.size();
  int inputpos = 0;

  Array ret;
  while (formatlen-- > 0) {
    char type = *(format++);
    char c;
    int arg = 1, argb;
    const char *name;
    int namelen;
    int size=0;

    /* Handle format arguments if any */
    if (formatlen > 0) {
      c = *format;

      if (c >= '0' && c <= '9') {
        arg = atoi(format);

        while (formatlen > 0 && *format >= '0' && *format <= '9') {
          format++;
          formatlen--;
        }
      } else if (c == '*') {
        arg = -1;
        format++;
        formatlen--;
      }
    }

    /* Get of new value in array */
    name = format;
    argb = arg;

    while (formatlen > 0 && *format != '/') {
      formatlen--;
      format++;
    }

    namelen = format - name;

    if (namelen > 200)
      namelen = 200;

    switch ((int) type) {
      /* Never use any input */
    case 'X':
      size = -1;
      break;

    case '@':
      size = 0;
      break;

    case 'a':
    case 'A':
      size = arg;
      arg = 1;
      break;

    case 'h':
    case 'H':
      size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
      arg = 1;
      break;

      /* Use 1 byte of input */
    case 'c':
    case 'C':
    case 'x':
      size = 1;
      break;

      /* Use 2 bytes of input */
    case 's':
    case 'S':
    case 'n':
    case 'v':
      size = 2;
      break;

      /* Use sizeof(int) bytes of input */
    case 'i':
    case 'I':
      size = sizeof(int);
      break;

      /* Use 4 bytes of input */
    case 'l':
    case 'L':
    case 'N':
    case 'V':
      size = 4;
      break;

      /* Use sizeof(float) bytes of input */
    case 'f':
      size = sizeof(float);
      break;

      /* Use sizeof(double) bytes of input */
    case 'd':
      size = sizeof(double);
      break;

    default:
      throw_invalid_argument("Invalid format type %c", type);
      return false;
    }

    /* Do actual unpacking */
    for (int i = 0; i != arg; i++ ) {
      /* Space for name + number, safe as namelen is ensured <= 200 */
      char n[256];

      if (arg != 1 || namelen == 0) {
        /* Need to add element number to name */
        snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
      } else {
        /* Truncate name to next format code or end of string */
        snprintf(n, sizeof(n), "%.*s", namelen, name);
      }

      if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
        throw_invalid_argument("Type %c: integer overflow", type);
        inputpos = 0;
      }

      if ((inputpos + size) <= inputlen) {
        switch ((int) type) {
        case 'a':
        case 'A': {
          char pad = (type == 'a') ? '\0' : ' ';
          int len = inputlen - inputpos; /* Remaining string */

          /* If size was given take minimum of len and size */
          if ((size >= 0) && (len > size)) {
            len = size;
          }

          size = len;

          /* Remove padding chars from unpacked data */
          while (--len >= 0) {
            if (input[inputpos + len] != pad)
              break;
          }

          ret.set(String(n, CopyString),
                  String(input + inputpos, len + 1, CopyString));
          break;
        }

        case 'h':
        case 'H': {
          int len = (inputlen - inputpos) * 2;  /* Remaining */
          int nibbleshift = (type == 'h') ? 0 : 4;
          int first = 1;
          char *buf;
          int ipos, opos;

          /* If size was given take minimum of len and size */
          if (size >= 0 && len > (size * 2)) {
            len = size * 2;
          }

          if (argb > 0) {
            len -= argb % 2;
          }

          String s = String(len, ReserveString);
          buf = s.bufferSlice().ptr;

          for (ipos = opos = 0; opos < len; opos++) {
            char c = (input[inputpos + ipos] >> nibbleshift) & 0xf;

            if (c < 10) {
              c += '0';
            } else {
              c += 'a' - 10;
            }

            buf[opos] = c;
            nibbleshift = (nibbleshift + 4) & 7;

            if (first-- == 0) {
              ipos++;
              first = 1;
            }
          }

          s.setSize(len);
          ret.set(String(n, CopyString), s);
          break;
        }

        case 'c':
        case 'C': {
          int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
          ret.set(String(n, CopyString),
                  unpack(&input[inputpos], 1, issigned, byte_map));
          break;
        }

        case 's':
        case 'S':
        case 'n':
        case 'v': {
          int issigned = 0;
          int *map = machine_endian_short_map;

          if (type == 's') {
            issigned = input[inputpos + (machine_little_endian ? 1 : 0)] &
              0x80;
          } else if (type == 'n') {
            map = big_endian_short_map;
          } else if (type == 'v') {
            map = little_endian_short_map;
          }

          ret.set(String(n, CopyString),
                  unpack(&input[inputpos], 2, issigned, map));
          break;
        }

        case 'i':
        case 'I': {
          int32_t v = 0;
          int issigned = 0;

          if (type == 'i') {
            issigned = input[inputpos + (machine_little_endian ?
                                         (sizeof(int) - 1) : 0)] & 0x80;
          } else if (sizeof(int32_t) > 4 &&
                     (input[inputpos + machine_endian_int32_map[3]]
                      & 0x80) == 0x80) {
            v = ~INT_MAX;
          }

          v |= unpack(&input[inputpos], sizeof(int), issigned, int_map);
          if (type == 'i') {
            ret.set(String(n, CopyString), v);
          } else {
            uint64_t u64 = uint32_t(v);
            ret.set(String(n, CopyString), u64);
          }
          break;
        }

        case 'l':
        case 'L':
        case 'N':
        case 'V': {
          int issigned = 0;
          int *map = machine_endian_int32_map;
          int32_t v = 0;

          if (type == 'l' || type == 'L') {
            issigned = input[inputpos + (machine_little_endian ? 3 : 0)]
              & 0x80;
          } else if (type == 'N') {
            issigned = input[inputpos] & 0x80;
            map = big_endian_int32_map;
          } else if (type == 'V') {
            issigned = input[inputpos + 3] & 0x80;
            map = little_endian_int32_map;
          }

          if (sizeof(int32_t) > 4 && issigned) {
            v = ~INT_MAX;
          }

          v |= unpack(&input[inputpos], 4, issigned, map);
          if (type == 'l') {
            ret.set(String(n, CopyString), v);
          } else {
            uint64_t u64 = uint32_t(v);
            ret.set(String(n, CopyString), u64);
          }
          break;
        }

        case 'f': {
          float v;

          memcpy(&v, &input[inputpos], sizeof(float));
          ret.set(String(n, CopyString), (double)v);
          break;
        }

        case 'd': {
          double v;

          memcpy(&v, &input[inputpos], sizeof(double));
          ret.set(String(n, CopyString), v);
          break;
        }

        case 'x':
          /* Do nothing with input, just skip it */
          break;

        case 'X':
          if (inputpos < size) {
            inputpos = -size;
            i = arg - 1;    /* Break out of for loop */

            if (arg >= 0) {
              throw_invalid_argument("Type %c: outside of string", type);
            }
          }
          break;

        case '@':
          if (arg <= inputlen) {
            inputpos = arg;
          } else {
            throw_invalid_argument("Type %c: outside of string", type);
          }

          i = arg - 1;  /* Done, break out of for loop */
          break;
        }

        inputpos += size;
        if (inputpos < 0) {
          if (size != -1) { /* only print warning if not working with * */
            throw_invalid_argument("Type %c: outside of string", type);
          }
          inputpos = 0;
        }
      } else if (arg < 0) {
        /* Reached end of input for '*' repeater */
        break;
      } else {
        throw_invalid_argument
          ("Type %c: not enough input, need %d, have %d",
           type, size, inputlen - inputpos);
        return false;
      }
    }

    formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
    format++;
  }
Example #24
0
Variant File::readRecord(const String& delimiter, int64_t maxlen /* = 0 */) {
    if (eof() && m_writepos == m_readpos) {
        return false;
    }

    if (maxlen <= 0 || maxlen > CHUNK_SIZE) {
        maxlen = CHUNK_SIZE;
    }

    int64_t avail = bufferedLen();
    if (m_buffer == nullptr) {
        m_buffer = (char *)malloc(CHUNK_SIZE * 3);
    }
    if (avail < maxlen && !eof()) {
        assert(m_writepos + maxlen - avail <= CHUNK_SIZE * 3);
        m_writepos += readImpl(m_buffer + m_writepos, maxlen - avail);
        maxlen = bufferedLen();
    }
    if (m_readpos >= CHUNK_SIZE) {
        memcpy(m_buffer, m_buffer + m_readpos, bufferedLen());
        m_writepos -= m_readpos;
        m_readpos = 0;
    }

    int64_t toread;
    const char *e;
    bool skip = false;
    if (delimiter.empty()) {
        toread = maxlen;
    } else {
        if (delimiter.size() == 1) {
            e = (const char *)memchr(m_buffer + m_readpos, delimiter.charAt(0),
                                     bufferedLen());
        } else {
            int64_t pos = string_find(m_buffer + m_readpos, bufferedLen(),
                                      delimiter.data(), delimiter.size(), 0, true);
            if (pos >= 0) {
                e = m_buffer + m_readpos + pos;
            } else {
                e = nullptr;
            }
        }

        if (!e) {
            toread = maxlen;
        } else {
            toread = e - m_buffer - m_readpos;
            skip = true;
        }
    }

    if (toread > maxlen && maxlen > 0) {
        toread = maxlen;
    }

    if (toread >= 0) {
        String s = String(toread, ReserveString);
        char *buf = s.bufferSlice().ptr;
        if (toread) {
            memcpy(buf, m_buffer + m_readpos, toread);
        }

        m_readpos += toread;
        if (skip) {
            m_readpos += delimiter.size();
            m_position += delimiter.size();
        }
        s.setSize(toread);
        return s;
    }

    return empty_string();
}