From 6c85e38775c06786556f1ed6f3ce878164b350b7 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Fri, 23 Jun 2017 21:24:58 +0900
Subject: [PATCH] Rewrite using rob instead of framebuf

---
 lib/Makefile.am                         |   4 +-
 lib/ngtcp2_conn.c                       |  94 ++++----------
 lib/ngtcp2_conn.h                       |  13 +-
 lib/ngtcp2_framebuf.c                   |  49 --------
 lib/ngtcp2_rob.c                        | 155 ++++++++++++++++++++++++
 lib/{ngtcp2_framebuf.h => ngtcp2_rob.h} |  41 +++++--
 6 files changed, 221 insertions(+), 135 deletions(-)
 delete mode 100644 lib/ngtcp2_framebuf.c
 create mode 100644 lib/ngtcp2_rob.c
 rename lib/{ngtcp2_framebuf.h => ngtcp2_rob.h} (57%)

diff --git a/lib/Makefile.am b/lib/Makefile.am
index d0291f59..f930ea46 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -40,7 +40,7 @@ OBJECTS = \
 	ngtcp2_conn.c \
 	ngtcp2_mem.c \
 	ngtcp2_pq.c \
-	ngtcp2_framebuf.c
+	ngtcp2_rob.c
 
 HFILES = \
 	ngtcp2_pkt.h \
@@ -51,7 +51,7 @@ HFILES = \
 	ngtcp2_conn.h \
 	ngtcp2_mem.h \
 	ngtcp2_pq.h \
-	ngtcp2_framebuf.h \
+	ngtcp2_rob.h \
 	ngtcp2_macro.h
 
 libngtcp2_la_SOURCES = $(HFILES) $(OBJECTS)
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index 110d739f..d55b1392 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -25,7 +25,6 @@
 #include "ngtcp2_conn.h"
 #include "ngtcp2_upe.h"
 #include "ngtcp2_pkt.h"
-#include "ngtcp2_framebuf.h"
 #include "ngtcp2_macro.h"
 
 static int ngtcp2_conn_new(ngtcp2_conn **pconn, uint64_t conn_id,
@@ -98,7 +97,7 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
     return;
   }
 
-  ngtcp2_strm_free(&conn->strm0, conn->mem);
+  ngtcp2_strm_free(&conn->strm0);
   ngtcp2_mem_free(conn->mem, conn);
 }
 
@@ -352,7 +351,7 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, const uint8_t *pkt,
     pktlen -= (size_t)nread;
 
     if (fr.type != NGTCP2_FRAME_STREAM || fr.stream.stream_id != 0 ||
-        conn->strm0.offset > fr.stream.offset) {
+        conn->strm0.offset >= fr.stream.offset + fr.stream.datalen) {
       continue;
     }
 
@@ -370,7 +369,7 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, const uint8_t *pkt,
         return rv;
       }
     } else {
-      rv = ngtcp2_conn_recv_reordering(conn, &conn->strm0, &fr.stream);
+      rv = ngtcp2_strm_recv_reordering(&conn->strm0, &fr.stream);
       if (rv != 0) {
         return rv;
       }
@@ -430,91 +429,46 @@ int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
   return -1;
 }
 
-int ngtcp2_conn_recv_reordering(ngtcp2_conn *conn, ngtcp2_strm *strm,
-                                ngtcp2_stream *fr) {
-  ngtcp2_framebuf *fb;
-  int rv;
-
-  if (strm->nbuffered >= 65536) {
-    return NGTCP2_ERR_INTERNAL_ERROR;
-  }
-
-  rv = ngtcp2_framebuf_new(&fb, fr, conn->mem);
-  if (rv != 0) {
-    return rv;
-  }
-
-  /* TODO This is not efficient.  Invent new way to store duplicated
-     buffered data */
-  strm->nbuffered += fr->datalen;
-
-  return ngtcp2_pq_push(&strm->pq, &fb->pq_entry);
-}
-
 int ngtcp2_conn_emit_pending_recv_handshake(ngtcp2_conn *conn,
                                             ngtcp2_strm *strm) {
-  ngtcp2_framebuf *fb;
-  uint64_t delta;
+  size_t datalen;
+  const uint8_t *data;
   int rv;
 
-  for (; !ngtcp2_pq_empty(&strm->pq);) {
-    fb = ngtcp2_struct_of(ngtcp2_pq_top(&strm->pq), ngtcp2_framebuf, pq_entry);
-
-    if (strm->offset < fb->fr.stream.offset) {
+  for (;;) {
+    datalen = ngtcp2_rob_data_at(&strm->rob, &data, strm->offset);
+    if (datalen == 0) {
       return 0;
     }
 
-    ngtcp2_pq_pop(&strm->pq);
-
-    delta = strm->offset - fb->fr.stream.offset;
-
-    if (delta < fb->fr.stream.datalen) {
-      rv = conn->callbacks.recv_handshake_data(conn, fb->fr.stream.data + delta,
-                                               fb->fr.stream.datalen - delta,
-                                               conn->user_data);
-      if (rv != 0) {
-        return rv;
-      }
+    rv = conn->callbacks.recv_handshake_data(conn, data, datalen,
+                                             conn->user_data);
+    if (rv != 0) {
+      return rv;
     }
 
-    ngtcp2_framebuf_del(fb, conn->mem);
+    ngtcp2_rob_pop(&strm->rob);
   }
-
-  return 0;
-}
-
-static int ngtcp2_stream_offset_less(const void *lhsx, const void *rhsx) {
-  const ngtcp2_framebuf *lhs, *rhs;
-
-  lhs = ngtcp2_struct_of(lhsx, ngtcp2_framebuf, pq_entry);
-  rhs = ngtcp2_struct_of(rhsx, ngtcp2_framebuf, pq_entry);
-
-  return lhs->fr.stream.offset < rhs->fr.stream.offset;
 }
 
 int ngtcp2_strm_init(ngtcp2_strm *strm, ngtcp2_mem *mem) {
   strm->offset = 0;
   strm->nbuffered = 0;
-  return ngtcp2_pq_init(&strm->pq, ngtcp2_stream_offset_less, mem);
+  strm->mem = mem;
+  return ngtcp2_rob_init(&strm->rob, mem);
 }
 
-static int ngtcp2_framebuf_item_free(ngtcp2_pq_entry *item, void *arg) {
-  ngtcp2_framebuf *fb;
-  ngtcp2_mem *mem;
-
-  fb = ngtcp2_struct_of(item, ngtcp2_framebuf, pq_entry);
-  mem = arg;
-
-  ngtcp2_framebuf_del(fb, mem);
-
-  return 0;
-}
-
-void ngtcp2_strm_free(ngtcp2_strm *strm, ngtcp2_mem *mem) {
+void ngtcp2_strm_free(ngtcp2_strm *strm) {
   if (strm == NULL) {
     return;
   }
+  ngtcp2_rob_free(&strm->rob);
+}
+
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, ngtcp2_stream *fr) {
+  if (strm->rob.bufferedlen >= 128 * 1024) {
+    return NGTCP2_ERR_INTERNAL_ERROR;
+  }
 
-  ngtcp2_pq_each(&strm->pq, ngtcp2_framebuf_item_free, mem);
-  ngtcp2_pq_free(&strm->pq);
+  return ngtcp2_rob_push(&strm->rob, fr->offset, fr->data, fr->datalen);
 }
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index 89de28c2..07c6021e 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -32,7 +32,8 @@
 #include <ngtcp2/ngtcp2.h>
 
 #include "ngtcp2_mem.h"
-#include "ngtcp2_pq.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_rob.h"
 
 typedef enum {
   /* Client specific handshake states */
@@ -52,13 +53,16 @@ typedef enum {
 
 typedef struct {
   uint64_t offset;
-  ngtcp2_pq pq;
+  ngtcp2_rob rob;
+  ngtcp2_mem *mem;
   size_t nbuffered;
 } ngtcp2_strm;
 
 int ngtcp2_strm_init(ngtcp2_strm *strm, ngtcp2_mem *mem);
 
-void ngtcp2_strm_free(ngtcp2_strm *strm, ngtcp2_mem *mem);
+void ngtcp2_strm_free(ngtcp2_strm *strm);
+
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, ngtcp2_stream *fr);
 
 struct ngtcp2_conn {
   int state;
@@ -72,9 +76,6 @@ struct ngtcp2_conn {
   int server;
 };
 
-int ngtcp2_conn_recv_reordering(ngtcp2_conn *conn, ngtcp2_strm *strm,
-                                ngtcp2_stream *fr);
-
 int ngtcp2_conn_emit_pending_recv_handshake(ngtcp2_conn *conn,
                                             ngtcp2_strm *strm);
 
diff --git a/lib/ngtcp2_framebuf.c b/lib/ngtcp2_framebuf.c
deleted file mode 100644
index 8bca8d3b..00000000
--- a/lib/ngtcp2_framebuf.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ngtcp2
- *
- * Copyright (c) 2017 ngtcp2 contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-#include "ngtcp2_framebuf.h"
-
-#include <string.h>
-
-int ngtcp2_framebuf_new(ngtcp2_framebuf **pfb, ngtcp2_stream *fr,
-                        ngtcp2_mem *mem) {
-  uint8_t *data;
-
-  *pfb = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_framebuf) + fr->datalen);
-  if (*pfb == NULL) {
-    return NGTCP2_ERR_NOMEM;
-  }
-
-  data = ((uint8_t *)*pfb) + sizeof(ngtcp2_framebuf);
-  memcpy(data, fr->data, fr->datalen);
-
-  (*pfb)->fr.stream = *fr;
-  (*pfb)->fr.stream.data = data;
-
-  return 0;
-}
-
-void ngtcp2_framebuf_del(ngtcp2_framebuf *fb, ngtcp2_mem *mem) {
-  ngtcp2_mem_free(mem, fb);
-}
diff --git a/lib/ngtcp2_rob.c b/lib/ngtcp2_rob.c
new file mode 100644
index 00000000..7d25d1fa
--- /dev/null
+++ b/lib/ngtcp2_rob.c
@@ -0,0 +1,155 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rob.h"
+
+#include <string.h>
+
+#include "ngtcp2_macro.h"
+
+int ngtcp2_rob_data_new(ngtcp2_rob_data **prdat, uint64_t offset,
+                        const uint8_t *data, size_t datalen, ngtcp2_mem *mem) {
+  uint8_t *dest;
+
+  *prdat = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + datalen);
+  if (*prdat == NULL) {
+    return NGTCP2_ERR_NOMEM;
+  }
+
+  dest = ((uint8_t *)*prdat) + sizeof(ngtcp2_rob_data);
+  memcpy(dest, data, datalen);
+
+  (*prdat)->offset = offset;
+  (*prdat)->data = dest;
+  (*prdat)->datalen = datalen;
+
+  return 0;
+}
+
+void ngtcp2_rob_data_del(ngtcp2_rob_data *rdat, ngtcp2_mem *mem) {
+  ngtcp2_mem_free(mem, rdat);
+}
+
+static int offset_less(const void *lhsx, const void *rhsx) {
+  const ngtcp2_rob_data *lhs, *rhs;
+
+  lhs = ngtcp2_struct_of(lhsx, ngtcp2_rob_data, pq_entry);
+  rhs = ngtcp2_struct_of(rhsx, ngtcp2_rob_data, pq_entry);
+
+  return lhs->offset < rhs->offset;
+}
+
+int ngtcp2_rob_init(ngtcp2_rob *rob, ngtcp2_mem *mem) {
+  int rv;
+
+  rv = ngtcp2_pq_init(&rob->pq, offset_less, mem);
+  if (rv != 0) {
+    return rv;
+  }
+
+  rob->bufferedlen = 0;
+  rob->mem = mem;
+
+  return 0;
+}
+
+static int pq_rob_data_free(ngtcp2_pq_entry *item, void *arg) {
+  ngtcp2_rob_data *rdat;
+  ngtcp2_rob *rob;
+
+  rdat = ngtcp2_struct_of(item, ngtcp2_rob_data, pq_entry);
+  rob = arg;
+
+  ngtcp2_rob_data_del(rdat, rob->mem);
+
+  return 0;
+}
+
+void ngtcp2_rob_free(ngtcp2_rob *rob) {
+  ngtcp2_pq_each(&rob->pq, pq_rob_data_free, rob);
+  ngtcp2_pq_free(&rob->pq);
+}
+
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+                    size_t datalen) {
+  ngtcp2_rob_data *rdat;
+  int rv;
+
+  rv = ngtcp2_rob_data_new(&rdat, offset, data, datalen, rob->mem);
+  if (rv != 0) {
+    return rv;
+  }
+
+  rv = ngtcp2_pq_push(&rob->pq, &rdat->pq_entry);
+  if (rv != 0) {
+    ngtcp2_rob_data_del(rdat, rob->mem);
+    return rv;
+  }
+
+  rob->bufferedlen += datalen;
+
+  return 0;
+}
+
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+                          uint64_t offset) {
+  ngtcp2_rob_data *rdat;
+  uint64_t delta;
+
+  for (; !ngtcp2_pq_empty(&rob->pq);) {
+    rdat = ngtcp2_struct_of(ngtcp2_pq_top(&rob->pq), ngtcp2_rob_data, pq_entry);
+    if (offset < rdat->offset) {
+      return 0;
+    }
+
+    delta = offset - rdat->offset;
+    if (delta >= rdat->datalen) {
+      ngtcp2_pq_pop(&rob->pq);
+      rob->bufferedlen -= rdat->datalen;
+
+      ngtcp2_rob_data_del(rdat, rob->mem);
+
+      continue;
+    }
+
+    *pdest = rdat->data + delta;
+    return rdat->datalen - delta;
+  }
+
+  return 0;
+}
+
+void ngtcp2_rob_pop(ngtcp2_rob *rob) {
+  ngtcp2_rob_data *rdat;
+
+  if (ngtcp2_pq_empty(&rob->pq)) {
+    return;
+  }
+
+  rdat = ngtcp2_struct_of(ngtcp2_pq_top(&rob->pq), ngtcp2_rob_data, pq_entry);
+  ngtcp2_pq_pop(&rob->pq);
+  rob->bufferedlen -= rdat->datalen;
+
+  ngtcp2_rob_data_del(rdat, rob->mem);
+}
diff --git a/lib/ngtcp2_framebuf.h b/lib/ngtcp2_rob.h
similarity index 57%
rename from lib/ngtcp2_framebuf.h
rename to lib/ngtcp2_rob.h
index 2cafd493..d10ee471 100644
--- a/lib/ngtcp2_framebuf.h
+++ b/lib/ngtcp2_rob.h
@@ -22,8 +22,8 @@
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
-#ifndef NGTCP2_FRAMEBUF_H
-#define NGTCP2_FRAMEBUF_H
+#ifndef NGTCP2_ROB_H
+#define NGTCP2_ROB_H
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -36,13 +36,38 @@
 
 typedef struct {
   ngtcp2_pq_entry pq_entry;
-  ngtcp2_frame fr;
+  uint64_t offset;
   uint8_t *data;
-} ngtcp2_framebuf;
+  size_t datalen;
+} ngtcp2_rob_data;
 
-int ngtcp2_framebuf_new(ngtcp2_framebuf **pfb, ngtcp2_stream *fr,
-                        ngtcp2_mem *mem);
+int ngtcp2_rob_data_new(ngtcp2_rob_data **prdat, uint64_t offset,
+                        const uint8_t *data, size_t datalen, ngtcp2_mem *mem);
 
-void ngtcp2_framebuf_del(ngtcp2_framebuf *fb, ngtcp2_mem *mem);
+void ngtcp2_rob_data_del(ngtcp2_rob_data *rdat, ngtcp2_mem *mem);
 
-#endif /* NGTCP2_FRAMEBUF_H */
+/*
+ * ngtcp2_rob reassembles stream data received in out of order.
+ *
+ * TODO The current implementation is very inefficient.  It should be
+ * redesigned to reduce memory foot print, and avoid dead lock issue.
+ */
+typedef struct {
+  ngtcp2_pq pq;
+  ngtcp2_mem *mem;
+  uint64_t bufferedlen;
+} ngtcp2_rob;
+
+int ngtcp2_rob_init(ngtcp2_rob *rob, ngtcp2_mem *mem);
+
+void ngtcp2_rob_free(ngtcp2_rob *rob);
+
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+                    size_t datalen);
+
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+                          uint64_t offset);
+
+void ngtcp2_rob_pop(ngtcp2_rob *rob);
+
+#endif /* NGTCP2_ROB_H */
-- 
GitLab