Browse Source

add asn1 encode decode. (#785)

caocao 3 weeks ago
parent
commit
39cbeab12c
3 changed files with 106 additions and 1 deletions
  1. 92 0
      base/hmath.h
  2. 2 1
      event/hloop.h
  3. 12 0
      event/unpack.c

+ 92 - 0
base/hmath.h

@@ -65,4 +65,96 @@ static inline long long varint_decode(const unsigned char* buf, int* len) {
     return ret;
 }
 
+static inline int asn1_encode(long long value, unsigned char* buf) {
+    /*
+     * unrolled for efficiency
+     * check each possibitlity of the 4 byte integer
+     */
+    unsigned char* p = buf;
+    if (value < 128)
+    {
+        *p = (unsigned char)value;
+        return 1;
+    }
+    else if (value < 256)
+    {
+        *p = 0x81;
+        p++;
+        *p = (unsigned char)value;
+        return 2;
+    }
+    else if (value < 65536)
+    {
+        *p = 0x82;
+        p++;
+        *p = (unsigned char)(value>>8);
+        p++;
+        *p = (unsigned char)value;
+        return 3;
+    }
+    else if (value < 16777126)
+    {
+        *p = 0x83;
+        p++;
+        *p = (unsigned char)(value>>16);
+        p++;
+        *p = (unsigned char)(value >> 8);
+        p++;
+        *p = (unsigned char)value;
+        return 4;
+    }
+    else
+    {
+        *p = 0x84;
+        p++;
+        *p = (unsigned char)(value >> 24);
+        p++;
+        *p = (unsigned char)(value >> 16);
+        p++;
+        *p = (unsigned char)(value >> 8);
+        p++;
+        *p = (unsigned char)value;
+        return 5;
+    }
+}
+
+static inline long long asn1_decode(const unsigned char* buf, int* len) {
+    long long ret = 0;
+    int bytes = 0;
+    unsigned int tag = 0, lenBytes = 0;
+    const unsigned char* p = buf;
+
+    tag = *p;
+    p++;
+    if (tag < 128) {
+        // Single-byte data
+        if (len) *len = 1;
+        return tag;
+    }
+    else if (tag == 0x80) {
+        // invalid data
+        if (len) *len = -1;
+        return 0;
+    }
+
+    // Multi-byte data
+    bytes++;
+
+    lenBytes = tag & 0x7F;
+
+    for (ret = 0; lenBytes > 0; lenBytes--) {
+        if (len && *len && bytes == *len) {
+            // Not enough length
+            *len = 0;
+            return 0;
+        }
+        ret = (ret << 8) | *p;
+        p++;
+        bytes++;
+    }
+
+    if (len) *len = bytes;
+    return ret;
+}
+
 #endif // HV_MATH_H_

+ 2 - 1
event/hloop.h

@@ -497,6 +497,7 @@ typedef enum {
     ENCODE_BY_VARINT        = 17,               // 1 MSB + 7 bits
     ENCODE_BY_LITTEL_ENDIAN = LITTLE_ENDIAN,    // 1234
     ENCODE_BY_BIG_ENDIAN    = BIG_ENDIAN,       // 4321
+    ENCODE_BY_ASN1          = 80,               // asn1 decode int
 } unpack_coding_e;
 
 typedef struct unpack_setting_s {
@@ -517,7 +518,7 @@ typedef struct unpack_setting_s {
          *
          * package_len = head_len + body_len + length_adjustment
          *
-         * if (length_field_coding == ENCODE_BY_VARINT) head_len = body_offset + varint_bytes - length_field_bytes;
+         * if (length_field_coding == ENCODE_BY_VARINT || length_field_coding == ENCODE_BY_ASN1) head_len = body_offset + varint_bytes - length_field_bytes;
          * else head_len = body_offset;
          *
          * length_field stores body length, exclude head length,

+ 12 - 0
event/unpack.c

@@ -139,6 +139,18 @@ int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes) {
                 return -1;
             }
             head_len = setting->body_offset + varint_bytes - setting->length_field_bytes;
+        }
+        else if (setting->length_field_coding == ENCODE_BY_ASN1) {
+            int varint_bytes = ep - lp;
+            body_len = asn1_decode(lp, &varint_bytes);
+            if (varint_bytes == 0) break;
+            if (varint_bytes == -1) {
+                hloge("varint is too big!");
+                io->error = ERR_OVER_LIMIT;
+                hio_close(io);
+                return -1;
+            }
+            head_len = setting->body_offset + varint_bytes - setting->length_field_bytes;
         } else {
             hloge("Unknown length_field_coding!");
             io->error = ERR_INVALID_PARAM;