Jelajahi Sumber

hio_readline, hio_readstring, hio_readbytes

ithewei 4 tahun lalu
induk
melakukan
c6acc7e0a4
12 mengubah file dengan 265 tambahan dan 88 penghapusan
  1. 7 0
      base/hbase.c
  2. 1 0
      base/hbase.h
  3. 23 1
      base/hbuf.h
  4. 3 2
      base/hplatform.h
  5. 6 0
      docs/API.md
  6. 123 7
      event/hevent.c
  7. 19 7
      event/hevent.h
  8. 13 1
      event/hloop.h
  9. 16 44
      event/nio.c
  10. 18 20
      event/unpack.c
  11. 20 0
      evpp/Channel.h
  12. 16 6
      examples/tcp_echo_server.c

+ 7 - 0
base/hbase.c

@@ -272,6 +272,13 @@ bool hv_islink(const char* path) {
 #endif
 }
 
+size_t hv_filesize(const char* filepath) {
+    struct stat st;
+    memset(&st, 0, sizeof(st));
+    stat(filepath, &st);
+    return st.st_size;
+}
+
 bool getboolean(const char* str) {
     if (str == NULL) return false;
     int len = strlen(str);

+ 1 - 0
base/hbase.h

@@ -78,6 +78,7 @@ HV_EXPORT bool hv_exists(const char* path);
 HV_EXPORT bool hv_isdir(const char* path);
 HV_EXPORT bool hv_isfile(const char* path);
 HV_EXPORT bool hv_islink(const char* path);
+HV_EXPORT size_t hv_filesize(const char* filepath);
 
 // 1 y on yes true enable
 HV_EXPORT bool getboolean(const char* str);

+ 23 - 1
base/hbuf.h

@@ -28,16 +28,38 @@ typedef struct offset_buf_s {
 #ifdef __cplusplus
     offset_buf_s() {
         base = NULL;
-        len = offset = 0;
+        len = 0;
+        offset = 0;
     }
 
     offset_buf_s(void* data, size_t len) {
         this->base = (char*)data;
         this->len = len;
+        offset = 0;
     }
 #endif
 } offset_buf_t;
 
+typedef struct fifo_buf_s {
+    char*  base;
+    size_t len;
+    size_t head;
+    size_t tail;
+#ifdef __cplusplus
+    fifo_buf_s() {
+        base = NULL;
+        len = 0;
+        head = tail = 0;
+    }
+
+    fifo_buf_s(void* data, size_t len) {
+        this->base = (char*)data;
+        this->len = len;
+        head = tail = 0;
+    }
+#endif
+} fifo_buf_t;
+
 #ifdef __cplusplus
 class HBuf : public hbuf_t {
 public:

+ 3 - 2
base/hplatform.h

@@ -222,9 +222,10 @@
 
 // BYTE_ORDER
 #ifndef BYTE_ORDER
-#if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(__ARMEL__)
+#if defined(ARCH_X86) || defined(ARCH_X86_64) || \
+    defined(__ARMEL__) || defined(__AARCH64EL__)
 #define BYTE_ORDER      LITTLE_ENDIAN
-#elif defined(__ARMEB__)
+#elif defined(__ARMEB__) || defined(__AARCH64EB__)
 #define BYTE_ORDER      BIG_ENDIAN
 #endif
 #endif

+ 6 - 0
docs/API.md

@@ -118,6 +118,7 @@
 - hv_isdir
 - hv_isfile
 - hv_islink
+- hv_filesize
 - getboolean
 - get_executable_path
 - get_executable_dir
@@ -398,6 +399,11 @@
 - hio_read_stop
 - hio_read_once
 - hio_read_until
+- hio_read_until_length
+- hio_read_until_delim
+- hio_read_readline
+- hio_read_readstring
+- hio_read_readbytes
 - hio_write
 - hio_close
 - hio_accept

+ 123 - 7
event/hevent.c

@@ -3,6 +3,8 @@
 #include "hatomic.h"
 #include "hlog.h"
 
+#include "unpack.h"
+
 uint64_t hloop_next_event_id() {
     static hatomic_t s_id = HATOMIC_VAR_INIT(0);
     return ++s_id;
@@ -99,9 +101,9 @@ void hio_ready(hio_t* io) {
     io->alloced_readbuf = 0;
     io->readbuf.base = io->loop->readbuf.base;
     io->readbuf.len = io->loop->readbuf.len;
-    io->readbuf.offset = 0;
-    io->read_once = 0;
-    io->read_until = 0;
+    io->readbuf.head = io->readbuf.tail = 0;
+    io->read_flags = 0;
+    io->read_until_length = 0;
     io->small_readbytes_cnt = 0;
     // write_queue
     io->write_queue_bytes = 0;
@@ -314,7 +316,81 @@ void hio_connect_cb(hio_t* io) {
     }
 }
 
+void hio_handle_read(hio_t* io, void* buf, int readbytes) {
+#if WITH_KCP
+    if (io->io_type == HIO_TYPE_KCP) {
+        hio_read_kcp(io, buf, readbytes);
+        return;
+    }
+#endif
+
+    if (io->unpack_setting) {
+        // hio_set_unpack
+        hio_unpack(io, buf, readbytes);
+    } else {
+        const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
+        const unsigned char* ep = (const unsigned char*)buf + readbytes;
+        if (io->read_flags & HIO_READ_UNTIL_LENGTH) {
+            // hio_read_until_length
+            if (ep - sp >= io->read_until_length) {
+                io->readbuf.head += io->read_until_length;
+                if (io->readbuf.head == io->readbuf.tail) {
+                    io->readbuf.head = io->readbuf.tail = 0;
+                }
+                io->readbuf.head = io->readbuf.tail = 0;
+                io->read_flags &= ~HIO_READ_UNTIL_LENGTH;
+                hio_read_cb(io, (void*)sp, io->read_until_length);
+            }
+        } else if (io->read_flags & HIO_READ_UNTIL_DELIM) {
+            // hio_read_until_delim
+            const unsigned char* p = (const unsigned char*)buf;
+            for (int i = 0; i < readbytes; ++i, ++p) {
+                if (*p == io->read_until_delim) {
+                    int len = p - sp + 1;
+                    io->readbuf.head += len;
+                    if (io->readbuf.head == io->readbuf.tail) {
+                        io->readbuf.head = io->readbuf.tail = 0;
+                    }
+                    io->read_flags &= ~HIO_READ_UNTIL_DELIM;
+                    hio_read_cb(io, (void*)sp, len);
+                    return;
+                }
+            }
+        } else {
+            // hio_read
+            io->readbuf.head = io->readbuf.tail = 0;
+            hio_read_cb(io, (void*)sp, ep - sp);
+        }
+    }
+
+    if (io->readbuf.head == io->readbuf.tail) {
+        io->readbuf.head = io->readbuf.tail = 0;
+    }
+    // readbuf autosize
+    if (io->readbuf.tail == io->readbuf.len) {
+        if (io->readbuf.head == 0) {
+            // scale up * 2
+            hio_alloc_readbuf(io, io->readbuf.len * 2);
+        } else {
+            // [head, tail] => [base, tail - head]
+            memmove(io->readbuf.base, io->readbuf.base + io->readbuf.head, io->readbuf.tail - io->readbuf.head);
+        }
+    } else {
+        size_t small_size = io->readbuf.len / 2;
+        if (io->readbuf.tail < small_size &&
+            io->small_readbytes_cnt >= 3) {
+            // scale down / 2
+            hio_alloc_readbuf(io, small_size);
+        }
+    }
+}
+
 void hio_read_cb(hio_t* io, void* buf, int len) {
+    if (io->read_flags & HIO_READ_ONCE) {
+        io->read_flags &= ~HIO_READ_ONCE;
+        hio_read_stop(io);
+    }
+
     if (io->read_cb) {
         // printd("read_cb------\n");
         io->read_cb(io, buf, len);
@@ -390,7 +466,7 @@ void hio_set_readbuf(hio_t* io, void* buf, size_t len) {
     hio_free_readbuf(io);
     io->readbuf.base = (char*)buf;
     io->readbuf.len = len;
-    io->readbuf.offset = 0;
+    io->readbuf.head = io->readbuf.tail = 0;
     io->alloced_readbuf = 0;
 }
 
@@ -502,6 +578,7 @@ void hio_alloc_readbuf(hio_t* io, int len) {
     }
     io->readbuf.len = len;
     io->alloced_readbuf = 1;
+    io->small_readbytes_cnt = 0;
 }
 
 void hio_free_readbuf(hio_t* io) {
@@ -515,12 +592,23 @@ void hio_free_readbuf(hio_t* io) {
 }
 
 int hio_read_once (hio_t* io) {
-    io->read_once = 1;
+    io->read_flags |= HIO_READ_ONCE;
     return hio_read_start(io);
 }
 
-int hio_read_until(hio_t* io, int len) {
-    io->read_until = len;
+int hio_read_until_length(hio_t* io, unsigned int len) {
+    if (len == 0) return 0;
+    if (io->readbuf.tail - io->readbuf.head >= len) {
+        void* buf = io->readbuf.base + io->readbuf.head;
+        io->readbuf.head += len;
+        if (io->readbuf.head == io->readbuf.tail) {
+            io->readbuf.head = io->readbuf.tail = 0;
+        }
+        hio_read_cb(io, buf, len);
+        return len;
+    }
+    io->read_flags = HIO_READ_UNTIL_LENGTH;
+    io->read_until_length = len;
     // NOTE: prepare readbuf
     if (hio_is_loop_readbuf(io) ||
         io->readbuf.len < len) {
@@ -529,6 +617,34 @@ int hio_read_until(hio_t* io, int len) {
     return hio_read_once(io);
 }
 
+int hio_read_until_delim(hio_t* io, unsigned char delim) {
+    if (io->readbuf.tail - io->readbuf.head > 0) {
+        const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
+        const unsigned char* ep = (const unsigned char*)io->readbuf.base + io->readbuf.tail;
+        const unsigned char* p = sp;
+        while (p <= ep) {
+            if (*p == delim) {
+                int len = p - sp + 1;
+                io->readbuf.head += len;
+                if (io->readbuf.head == io->readbuf.tail) {
+                    io->readbuf.head = io->readbuf.tail = 0;
+                }
+                hio_read_cb(io, (void*)sp, len);
+                return len;
+            }
+            ++p;
+        }
+    }
+    io->read_flags = HIO_READ_UNTIL_DELIM;
+    io->read_until_length = delim;
+    // NOTE: prepare readbuf
+    if (hio_is_loop_readbuf(io) ||
+        io->readbuf.len < HLOOP_READ_BUFSIZE) {
+        hio_alloc_readbuf(io, HLOOP_READ_BUFSIZE);
+    }
+    return hio_read_once(io);
+}
+
 //-----------------unpack---------------------------------------------
 void hio_set_unpack(hio_t* io, unpack_setting_t* setting) {
     hio_unset_unpack(io);

+ 19 - 7
event/hevent.h

@@ -17,6 +17,11 @@
 #define READ_BUFSIZE_HIGH_WATER     65536       // 64K
 #define WRITE_QUEUE_HIGH_WATER      (1U << 23)  // 8M
 
+// hio_read_flags
+#define HIO_READ_ONCE           0x1
+#define HIO_READ_UNTIL_LENGTH   0x2
+#define HIO_READ_UNTIL_DELIM    0x4
+
 ARRAY_DECL(hio_t*, io_array);
 QUEUE_DECL(hevent_t, event_queue);
 
@@ -90,7 +95,7 @@ struct hperiod_s {
 };
 
 QUEUE_DECL(offset_buf_t, write_queue);
-// sizeof(struct hio_s)=344 on linux-x64
+// sizeof(struct hio_s)=360 on linux-x64
 struct hio_s {
     HEVENT_FIELDS
     // flags
@@ -104,8 +109,7 @@ struct hio_s {
     unsigned    recvfrom    :1;
     unsigned    sendto      :1;
     unsigned    close       :1;
-    unsigned    read_once   :1;     // for hio_read_once
-    unsigned    alloced_readbuf :1; // for hio_read_until, hio_set_unpack
+    unsigned    alloced_readbuf :1; // for hio_alloc_readbuf
 // public:
     hio_type_e  io_type;
     uint32_t    id; // fd cannot be used as unique identifier, so we provide an id
@@ -115,11 +119,18 @@ struct hio_s {
     int         revents;
     struct sockaddr*    localaddr;
     struct sockaddr*    peeraddr;
-    offset_buf_t        readbuf;        // for read
-    int                 read_until;     // for hio_read_until
+    // read
+    fifo_buf_t          readbuf;
+    unsigned int        read_flags;
+    // for hio_read_until
+    union {
+        unsigned int    read_until_length;
+        unsigned char   read_until_delim;
+    };
     uint32_t            small_readbytes_cnt; // for readbuf autosize
-    struct write_queue  write_queue;    // for write
-    hrecursive_mutex_t  write_mutex;    // lock write and write_queue
+    // write
+    struct write_queue  write_queue;
+    hrecursive_mutex_t  write_mutex; // lock write and write_queue
     uint32_t            write_queue_bytes;
     // callbacks
     hread_cb    read_cb;
@@ -180,6 +191,7 @@ uint32_t hio_next_id();
 
 void hio_accept_cb(hio_t* io);
 void hio_connect_cb(hio_t* io);
+void hio_handle_read(hio_t* io, void* buf, int readbytes);
 void hio_read_cb(hio_t* io, void* buf, int len);
 void hio_write_cb(hio_t* io, const void* buf, int len);
 void hio_close_cb(hio_t* io);

+ 13 - 1
event/hloop.h

@@ -302,18 +302,30 @@ HV_EXPORT void hio_set_heartbeat(hio_t* io, int interval_ms, hio_send_heartbeat_
 // Nonblocking, poll IO events in the loop to call corresponding callback.
 // hio_add(io, HV_READ) => accept => haccept_cb
 HV_EXPORT int hio_accept (hio_t* io);
+
 // connect => hio_add(io, HV_WRITE) => hconnect_cb
 HV_EXPORT int hio_connect(hio_t* io);
+
 // hio_add(io, HV_READ) => read => hread_cb
 HV_EXPORT int hio_read   (hio_t* io);
 #define hio_read_start(io) hio_read(io)
 #define hio_read_stop(io)  hio_del(io, HV_READ)
+
 // hio_read_start => hread_cb => hio_read_stop
 HV_EXPORT int hio_read_once (hio_t* io);
-HV_EXPORT int hio_read_until(hio_t* io, int len);
+// hio_read_once => hread_cb(len)
+HV_EXPORT int hio_read_until_length(hio_t* io, unsigned int len);
+// hio_read_once => hread_cb(...delim)
+HV_EXPORT int hio_read_until_delim (hio_t* io, unsigned char delim);
+#define hio_readline(io)        hio_read_until_delim(io, '\n')
+#define hio_readstring(io)      hio_read_until_delim(io, '\0')
+#define hio_readbytes(io, len)  hio_read_until_length(io, len)
+#define hio_read_until(io, len) hio_read_until_length(io, len)
+
 // NOTE: hio_write is thread-safe, locked by recursive_mutex, allow to be called by other threads.
 // hio_try_write => hio_add(io, HV_WRITE) => write => hwrite_cb
 HV_EXPORT int hio_write  (hio_t* io, const void* buf, size_t len);
+
 // NOTE: hio_close is thread-safe, hio_close_async will be called actually in other thread.
 // hio_del(io, HV_RDWR) => close => hclose_cb
 HV_EXPORT int hio_close  (hio_t* io);

+ 16 - 44
event/nio.c

@@ -5,7 +5,6 @@
 #include "hssl.h"
 #include "hlog.h"
 #include "hthread.h"
-#include "unpack.h"
 
 static void __connect_timeout_cb(htimer_t* timer) {
     hio_t* io = (hio_t*)timer->privdata;
@@ -47,30 +46,7 @@ static void __read_cb(hio_t* io, void* buf, int readbytes) {
     if (io->keepalive_timer) {
         htimer_reset(io->keepalive_timer);
     }
-
-    if (io->unpack_setting) {
-        hio_unpack(io, buf, readbytes);
-    } else {
-        if (io->read_once) {
-            hio_read_stop(io);
-        }
-
-#if WITH_KCP
-        if (io->io_type == HIO_TYPE_KCP) {
-            hio_read_kcp(io, buf, readbytes);
-            return;
-        }
-#endif
-        hio_read_cb(io, buf, readbytes);
-    }
-
-    // readbuf autosize
-    if (io->small_readbytes_cnt >= 3) {
-        io->small_readbytes_cnt = 0;
-        size_t small_size = io->readbuf.len / 2;
-        io->readbuf.base = (char*)safe_realloc(io->readbuf.base, small_size, io->readbuf.len);
-        io->readbuf.len = small_size;
-    }
+    hio_handle_read(io, buf, readbytes);
 }
 
 static void __write_cb(hio_t* io, const void* buf, int writebytes) {
@@ -288,12 +264,13 @@ static void nio_read(hio_t* io) {
     void* buf;
     int len = 0, nread = 0, err = 0;
 read:
-    buf = io->readbuf.base + io->readbuf.offset;
-    if (io->read_until) {
-        len = io->read_until;
+    buf = io->readbuf.base + io->readbuf.tail;
+    if (io->read_flags & HIO_READ_UNTIL_LENGTH) {
+        len = io->read_until_length - (io->readbuf.tail - io->readbuf.head);
     } else {
-        len = io->readbuf.len - io->readbuf.offset;
+        len = io->readbuf.len - io->readbuf.tail;
     }
+    assert(len > 0);
     nread = __nio_read(io, buf, len);
     // printd("read retval=%d\n", nread);
     if (nread < 0) {
@@ -313,19 +290,9 @@ read:
     if (nread == 0) {
         goto disconnect;
     }
-    if (io->read_until) {
-        io->readbuf.offset += nread;
-        io->read_until -= nread;
-        if (io->read_until == 0) {
-            __read_cb(io, io->readbuf.base, io->readbuf.offset);
-            io->readbuf.offset = 0;
-        }
-    } else {
-        __read_cb(io, buf, nread);
-        if (nread == len) {
-            goto read;
-        }
-    }
+    io->readbuf.tail += nread;
+    __read_cb(io, buf, nread);
+    // if (nread == len) goto read;
     return;
 read_error:
 disconnect:
@@ -542,8 +509,13 @@ enqueue:
 write_error:
 disconnect:
     hrecursive_mutex_unlock(&io->write_mutex);
-    hio_close(io);
-    return nwrite;
+    /* NOTE:
+     * We usually free resources in hclose_cb,
+     * if hio_close_sync, we have to be very careful to avoid using freed resources.
+     * But if hio_close_async, we do not have to worry about this.
+     */
+    hio_close_async(io);
+    return nwrite < 0 ? nwrite : -1;
 }
 
 int hio_close (hio_t* io) {

+ 18 - 20
event/unpack.c

@@ -20,9 +20,8 @@ int hio_unpack(hio_t* io, void* buf, int readbytes) {
 }
 
 int hio_unpack_by_fixed_length(hio_t* io, void* buf, int readbytes) {
-    const unsigned char* sp = (const unsigned char*)io->readbuf.base;
-    assert(buf == sp + io->readbuf.offset);
-    const unsigned char* ep = sp + io->readbuf.offset + readbytes;
+    const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
+    const unsigned char* ep = (const unsigned char*)buf + readbytes;
     unpack_setting_t* setting = io->unpack_setting;
 
     int fixed_length = setting->fixed_length;
@@ -38,7 +37,8 @@ int hio_unpack_by_fixed_length(hio_t* io, void* buf, int readbytes) {
         remain -= fixed_length;
     }
 
-    io->readbuf.offset = remain;
+    io->readbuf.head = 0;
+    io->readbuf.tail = remain;
     if (remain) {
         // [p, p+remain] => [base, base+remain]
         if (p != (unsigned char*)io->readbuf.base) {
@@ -50,16 +50,14 @@ int hio_unpack_by_fixed_length(hio_t* io, void* buf, int readbytes) {
 }
 
 int hio_unpack_by_delimiter(hio_t* io, void* buf, int readbytes) {
-    const unsigned char* sp = (const unsigned char*)io->readbuf.base;
-    assert(buf == sp + io->readbuf.offset);
-    const unsigned char* ep = sp + io->readbuf.offset + readbytes;
+    const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
+    const unsigned char* ep = (const unsigned char*)buf + readbytes;
     unpack_setting_t* setting = io->unpack_setting;
 
     unsigned char* delimiter = setting->delimiter;
     int delimiter_bytes = setting->delimiter_bytes;
 
-    // [offset - package_eof_bytes + 1, offset + readbytes]
-    const unsigned char* p = sp + io->readbuf.offset - delimiter_bytes + 1;
+    const unsigned char* p = (const unsigned char*)buf - delimiter_bytes + 1;
     if (p < sp) p = sp;
     int remain = ep - p;
     int handled = 0;
@@ -83,21 +81,22 @@ not_match:
     }
 
     remain = ep - sp;
-    io->readbuf.offset = remain;
+    io->readbuf.head = 0;
+    io->readbuf.tail = remain;
     if (remain) {
         // [sp, sp+remain] => [base, base+remain]
         if (sp != (unsigned char*)io->readbuf.base) {
             memmove(io->readbuf.base, sp, remain);
         }
-        if (io->readbuf.offset == io->readbuf.len) {
+        if (io->readbuf.tail == io->readbuf.len) {
             if (io->readbuf.len >= setting->package_max_length) {
                 hloge("recv package over %d bytes!", (int)setting->package_max_length);
                 io->error = ERR_OVER_LIMIT;
                 hio_close(io);
                 return -1;
             }
-            io->readbuf.len = MIN(io->readbuf.len * 2, setting->package_max_length);
-            io->readbuf.base = (char*)safe_realloc(io->readbuf.base, io->readbuf.len, io->readbuf.offset);
+            int newsize = MIN(io->readbuf.len * 2, setting->package_max_length);
+            hio_alloc_readbuf(io, newsize);
         }
     }
 
@@ -105,9 +104,8 @@ not_match:
 }
 
 int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes) {
-    const unsigned char* sp = (const unsigned char*)io->readbuf.base;
-    assert(buf == sp + io->readbuf.offset);
-    const unsigned char* ep = sp + io->readbuf.offset + readbytes;
+    const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
+    const unsigned char* ep = (const unsigned char*)buf + readbytes;
     unpack_setting_t* setting = io->unpack_setting;
 
     const unsigned char* p = sp;
@@ -153,7 +151,8 @@ int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes) {
         }
     }
 
-    io->readbuf.offset = remain;
+    io->readbuf.head = 0;
+    io->readbuf.tail = remain;
     if (remain) {
         // [p, p+remain] => [base, base+remain]
         if (p != (unsigned char*)io->readbuf.base) {
@@ -166,9 +165,8 @@ int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes) {
                 hio_close(io);
                 return -1;
             }
-            io->readbuf.len *= 2;
-            io->readbuf.len = LIMIT(package_len, io->readbuf.len, setting->package_max_length);
-            io->readbuf.base = (char*)safe_realloc(io->readbuf.base, io->readbuf.len, io->readbuf.offset);
+            int newsize = LIMIT(package_len, io->readbuf.len * 2, setting->package_max_length);
+            hio_alloc_readbuf(io, newsize);
         }
     }
 

+ 20 - 0
evpp/Channel.h

@@ -88,6 +88,26 @@ public:
         return hio_read_stop(io_);
     }
 
+    int readOnce() {
+        if (!isOpened()) return -1;
+        return hio_read_once(io_);
+    }
+
+    int readString() {
+        if (!isOpened()) return -1;
+        return hio_readstring(io_);
+    }
+
+    int readLine() {
+        if (!isOpened()) return -1;
+        return hio_readline(io_);
+    }
+
+    int readBytes(int len) {
+        if (!isOpened() || len <= 0) return -1;
+        return hio_readbytes(io_, len);
+    }
+
     int write(const void* data, int size) {
         if (!isOpened()) return -1;
         return hio_write(io_, data, size);

+ 16 - 6
examples/tcp_echo_server.c

@@ -21,12 +21,14 @@
  */
 #define TEST_SSL        0
 #define TEST_READ_ONCE  0
-#define TEST_READ_UNTIL 0
+#define TEST_READLINE   0
+#define TEST_READSTRING 0
+#define TEST_READBYTES  0
 #define TEST_READ_STOP  0
 #define TEST_UNPACK     0
 
 #if TEST_UNPACK
-unpack_setting_t unpack_setting;
+static unpack_setting_t unpack_setting;
 #endif
 
 // hloop_create_tcp_server -> on_accept -> hio_read -> on_recv -> hio_write
@@ -51,8 +53,12 @@ static void on_recv(hio_t* io, void* buf, int readbytes) {
     hio_read_stop(io);
 #elif TEST_READ_ONCE
     hio_read_once(io);
-#elif TEST_READ_UNTIL
-    hio_read_until(io, TEST_READ_UNTIL);
+#elif TEST_READLINE
+    hio_readline(io);
+#elif TEST_READSTRING
+    hio_readstring(io);
+#elif TEST_READBYTES
+    hio_readbytes(io, TEST_READBYTES);
 #endif
 }
 
@@ -73,8 +79,12 @@ static void on_accept(hio_t* io) {
 
 #if TEST_READ_ONCE
     hio_read_once(io);
-#elif TEST_READ_UNTIL
-    hio_read_until(io, TEST_READ_UNTIL);
+#elif TEST_READLINE
+    hio_readline(io);
+#elif TEST_READSTRING
+    hio_readstring(io);
+#elif TEST_READBYTES
+    hio_readbytes(io, TEST_READBYTES);
 #else
     hio_read_start(io);
 #endif