multi_compress 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/GET_STARTED.md +19 -0
  4. data/README.md +14 -1
  5. data/db_core/include/mcdb_format.h +67 -9
  6. data/db_core/src/mcdb_format.c +392 -139
  7. data/db_deployment/mysql/README.md +19 -0
  8. data/db_deployment/mysql/bin/common +70 -18
  9. data/db_deployment/mysql/bin/enable +5 -1
  10. data/db_deployment/mysql/bin/status +2 -0
  11. data/db_deployment/mysql/bin/upgrade +17 -5
  12. data/db_deployment/postgres/README.md +24 -0
  13. data/db_deployment/postgres/bin/enable +2 -0
  14. data/db_deployment/postgres/bin/install +4 -2
  15. data/docs/database-envelope-v2.md +147 -0
  16. data/docs/rfcs/0002-mcdb2-dictionaries.md +75 -0
  17. data/ext/multi_compress/multi_compress.c +32 -0
  18. data/lib/multi_compress/database.rb +255 -49
  19. data/lib/multi_compress/db_deployment.rb +423 -11
  20. data/lib/multi_compress/version.rb +1 -1
  21. data/mysql_udf/README.md +45 -0
  22. data/mysql_udf/sql/examples.sql +5 -0
  23. data/mysql_udf/sql/install.sql +25 -1
  24. data/mysql_udf/sql/uninstall.sql +7 -1
  25. data/mysql_udf/sql/views.sql +16 -4
  26. data/mysql_udf/src/multi_compress_mysql.c +383 -44
  27. data/mysql_udf/test/run_e2e.sh +119 -1
  28. data/postgres_extension/Makefile +2 -2
  29. data/postgres_extension/README.md +48 -0
  30. data/postgres_extension/multi_compress.control +2 -2
  31. data/postgres_extension/multi_compress_pg.exports +12 -0
  32. data/postgres_extension/sql/examples.sql +11 -0
  33. data/postgres_extension/sql/multi_compress--0.5.0--0.6.0.sql +59 -0
  34. data/postgres_extension/sql/multi_compress--0.6.0.sql +85 -0
  35. data/postgres_extension/src/multi_compress_pg.c +301 -16
  36. data/postgres_extension/test/run_e2e.sh +90 -2
  37. metadata +6 -2
@@ -1,63 +1,29 @@
1
1
  #include "mcdb_format.h"
2
2
 
3
+ #include <stdio.h>
3
4
  #include <stdlib.h>
4
5
  #include <string.h>
5
- #include <stdio.h>
6
6
 
7
7
  #include <zstd.h>
8
8
 
9
- typedef char
10
- mcdb_envelope_bound_check[(MCDB_MAX_ENVELOPE ==
11
- (unsigned)(MCDB_HEADER_SIZE + ZSTD_COMPRESSBOUND(MCDB_MAX_OUTPUT)))
12
- ? 1
13
- : -1];
14
-
15
- static const uint32_t MCDB_CRC32_TABLE[256] = {
16
- 0x00000000U, 0x77073096U, 0xEE0E612CU, 0x990951BAU, 0x076DC419U, 0x706AF48FU, 0xE963A535U,
17
- 0x9E6495A3U, 0x0EDB8832U, 0x79DCB8A4U, 0xE0D5E91EU, 0x97D2D988U, 0x09B64C2BU, 0x7EB17CBDU,
18
- 0xE7B82D07U, 0x90BF1D91U, 0x1DB71064U, 0x6AB020F2U, 0xF3B97148U, 0x84BE41DEU, 0x1ADAD47DU,
19
- 0x6DDDE4EBU, 0xF4D4B551U, 0x83D385C7U, 0x136C9856U, 0x646BA8C0U, 0xFD62F97AU, 0x8A65C9ECU,
20
- 0x14015C4FU, 0x63066CD9U, 0xFA0F3D63U, 0x8D080DF5U, 0x3B6E20C8U, 0x4C69105EU, 0xD56041E4U,
21
- 0xA2677172U, 0x3C03E4D1U, 0x4B04D447U, 0xD20D85FDU, 0xA50AB56BU, 0x35B5A8FAU, 0x42B2986CU,
22
- 0xDBBBC9D6U, 0xACBCF940U, 0x32D86CE3U, 0x45DF5C75U, 0xDCD60DCFU, 0xABD13D59U, 0x26D930ACU,
23
- 0x51DE003AU, 0xC8D75180U, 0xBFD06116U, 0x21B4F4B5U, 0x56B3C423U, 0xCFBA9599U, 0xB8BDA50FU,
24
- 0x2802B89EU, 0x5F058808U, 0xC60CD9B2U, 0xB10BE924U, 0x2F6F7C87U, 0x58684C11U, 0xC1611DABU,
25
- 0xB6662D3DU, 0x76DC4190U, 0x01DB7106U, 0x98D220BCU, 0xEFD5102AU, 0x71B18589U, 0x06B6B51FU,
26
- 0x9FBFE4A5U, 0xE8B8D433U, 0x7807C9A2U, 0x0F00F934U, 0x9609A88EU, 0xE10E9818U, 0x7F6A0DBBU,
27
- 0x086D3D2DU, 0x91646C97U, 0xE6635C01U, 0x6B6B51F4U, 0x1C6C6162U, 0x856530D8U, 0xF262004EU,
28
- 0x6C0695EDU, 0x1B01A57BU, 0x8208F4C1U, 0xF50FC457U, 0x65B0D9C6U, 0x12B7E950U, 0x8BBEB8EAU,
29
- 0xFCB9887CU, 0x62DD1DDFU, 0x15DA2D49U, 0x8CD37CF3U, 0xFBD44C65U, 0x4DB26158U, 0x3AB551CEU,
30
- 0xA3BC0074U, 0xD4BB30E2U, 0x4ADFA541U, 0x3DD895D7U, 0xA4D1C46DU, 0xD3D6F4FBU, 0x4369E96AU,
31
- 0x346ED9FCU, 0xAD678846U, 0xDA60B8D0U, 0x44042D73U, 0x33031DE5U, 0xAA0A4C5FU, 0xDD0D7CC9U,
32
- 0x5005713CU, 0x270241AAU, 0xBE0B1010U, 0xC90C2086U, 0x5768B525U, 0x206F85B3U, 0xB966D409U,
33
- 0xCE61E49FU, 0x5EDEF90EU, 0x29D9C998U, 0xB0D09822U, 0xC7D7A8B4U, 0x59B33D17U, 0x2EB40D81U,
34
- 0xB7BD5C3BU, 0xC0BA6CADU, 0xEDB88320U, 0x9ABFB3B6U, 0x03B6E20CU, 0x74B1D29AU, 0xEAD54739U,
35
- 0x9DD277AFU, 0x04DB2615U, 0x73DC1683U, 0xE3630B12U, 0x94643B84U, 0x0D6D6A3EU, 0x7A6A5AA8U,
36
- 0xE40ECF0BU, 0x9309FF9DU, 0x0A00AE27U, 0x7D079EB1U, 0xF00F9344U, 0x8708A3D2U, 0x1E01F268U,
37
- 0x6906C2FEU, 0xF762575DU, 0x806567CBU, 0x196C3671U, 0x6E6B06E7U, 0xFED41B76U, 0x89D32BE0U,
38
- 0x10DA7A5AU, 0x67DD4ACCU, 0xF9B9DF6FU, 0x8EBEEFF9U, 0x17B7BE43U, 0x60B08ED5U, 0xD6D6A3E8U,
39
- 0xA1D1937EU, 0x38D8C2C4U, 0x4FDFF252U, 0xD1BB67F1U, 0xA6BC5767U, 0x3FB506DDU, 0x48B2364BU,
40
- 0xD80D2BDAU, 0xAF0A1B4CU, 0x36034AF6U, 0x41047A60U, 0xDF60EFC3U, 0xA867DF55U, 0x316E8EEFU,
41
- 0x4669BE79U, 0xCB61B38CU, 0xBC66831AU, 0x256FD2A0U, 0x5268E236U, 0xCC0C7795U, 0xBB0B4703U,
42
- 0x220216B9U, 0x5505262FU, 0xC5BA3BBEU, 0xB2BD0B28U, 0x2BB45A92U, 0x5CB36A04U, 0xC2D7FFA7U,
43
- 0xB5D0CF31U, 0x2CD99E8BU, 0x5BDEAE1DU, 0x9B64C2B0U, 0xEC63F226U, 0x756AA39CU, 0x026D930AU,
44
- 0x9C0906A9U, 0xEB0E363FU, 0x72076785U, 0x05005713U, 0x95BF4A82U, 0xE2B87A14U, 0x7BB12BAEU,
45
- 0x0CB61B38U, 0x92D28E9BU, 0xE5D5BE0DU, 0x7CDCEFB7U, 0x0BDBDF21U, 0x86D3D2D4U, 0xF1D4E242U,
46
- 0x68DDB3F8U, 0x1FDA836EU, 0x81BE16CDU, 0xF6B9265BU, 0x6FB077E1U, 0x18B74777U, 0x88085AE6U,
47
- 0xFF0F6A70U, 0x66063BCAU, 0x11010B5CU, 0x8F659EFFU, 0xF862AE69U, 0x616BFFD3U, 0x166CCF45U,
48
- 0xA00AE278U, 0xD70DD2EEU, 0x4E048354U, 0x3903B3C2U, 0xA7672661U, 0xD06016F7U, 0x4969474DU,
49
- 0x3E6E77DBU, 0xAED16A4AU, 0xD9D65ADCU, 0x40DF0B66U, 0x37D83BF0U, 0xA9BCAE53U, 0xDEBB9EC5U,
50
- 0x47B2CF7FU, 0x30B5FFE9U, 0xBDBDF21CU, 0xCABAC28AU, 0x53B39330U, 0x24B4A3A6U, 0xBAD03605U,
51
- 0xCDD70693U, 0x54DE5729U, 0x23D967BFU, 0xB3667A2EU, 0xC4614AB8U, 0x5D681B02U, 0x2A6F2B94U,
52
- 0xB40BBE37U, 0xC30C8EA1U, 0x5A05DF1BU, 0x2D02EF8DU,
53
- };
9
+ typedef char mcdb_v1_envelope_bound_check
10
+ [(MCDB_MAX_ENVELOPE_V1 == (unsigned)(MCDB_V1_HEADER_SIZE + ZSTD_COMPRESSBOUND(MCDB_MAX_OUTPUT)))
11
+ ? 1
12
+ : -1];
13
+ typedef char mcdb_v2_envelope_bound_check
14
+ [(MCDB_MAX_ENVELOPE_V2 == (unsigned)(MCDB_V2_HEADER_SIZE + ZSTD_COMPRESSBOUND(MCDB_MAX_OUTPUT)))
15
+ ? 1
16
+ : -1];
54
17
 
55
18
  static uint32_t mcdb_crc32(const unsigned char *data, size_t len) {
56
19
  uint32_t crc = 0xFFFFFFFFU;
57
20
  size_t i;
21
+ int bit;
58
22
 
59
23
  for (i = 0; i < len; i++) {
60
- crc = MCDB_CRC32_TABLE[(crc ^ data[i]) & 0xFFU] ^ (crc >> 8);
24
+ crc ^= data[i];
25
+ for (bit = 0; bit < 8; bit++)
26
+ crc = (crc & 1U) ? ((crc >> 1) ^ 0xEDB88320U) : (crc >> 1);
61
27
  }
62
28
  return crc ^ 0xFFFFFFFFU;
63
29
  }
@@ -65,7 +31,6 @@ static uint32_t mcdb_crc32(const unsigned char *data, size_t len) {
65
31
  static uint64_t read_u64_le(const unsigned char *p) {
66
32
  uint64_t v = 0;
67
33
  int i;
68
-
69
34
  for (i = 0; i < 8; i++)
70
35
  v |= (uint64_t)p[i] << (8 * i);
71
36
  return v;
@@ -77,122 +42,169 @@ static uint32_t read_u32_le(const unsigned char *p) {
77
42
 
78
43
  static int mcdb_is_valid_utf8(const unsigned char *s, size_t len) {
79
44
  size_t i = 0;
80
-
81
45
  while (i < len) {
82
- unsigned char c;
46
+ unsigned char c = s[i];
83
47
  size_t n;
84
- uint32_t cp;
85
- unsigned char lo2;
86
- unsigned char hi2;
48
+ unsigned char lo2 = 0x80;
49
+ unsigned char hi2 = 0xBF;
87
50
  size_t k;
88
51
 
89
- c = s[i];
90
- n = 0;
91
- cp = 0;
92
- lo2 = 0x80;
93
- hi2 = 0xBF;
94
-
95
52
  if (c < 0x80) {
96
53
  if (c == 0)
97
54
  return 0;
98
- i += 1;
55
+ i++;
99
56
  continue;
100
57
  }
101
-
102
58
  if ((c & 0xE0) == 0xC0) {
103
- n = 2;
104
- cp = c & 0x1F;
105
59
  if (c < 0xC2)
106
60
  return 0;
61
+ n = 2;
107
62
  } else if ((c & 0xF0) == 0xE0) {
108
63
  n = 3;
109
- cp = c & 0x0F;
110
64
  if (c == 0xE0)
111
- lo2 = 0xA0; /* no overlong */
65
+ lo2 = 0xA0;
112
66
  if (c == 0xED)
113
- hi2 = 0x9F; /* no surrogates */
67
+ hi2 = 0x9F;
114
68
  } else if ((c & 0xF8) == 0xF0) {
115
- n = 4;
116
- cp = c & 0x07;
117
- if (c < 0xF0 || c > 0xF4)
69
+ if (c > 0xF4)
118
70
  return 0;
71
+ n = 4;
119
72
  if (c == 0xF0)
120
- lo2 = 0x90; /* no overlong */
73
+ lo2 = 0x90;
121
74
  if (c == 0xF4)
122
- hi2 = 0x8F; /* <= U+10FFFF */
75
+ hi2 = 0x8F;
123
76
  } else {
124
77
  return 0;
125
78
  }
126
-
127
79
  if (i + n > len)
128
80
  return 0;
129
81
  for (k = 1; k < n; k++) {
130
- unsigned char cc;
131
- unsigned char lo;
132
- unsigned char hi;
133
-
134
- cc = s[i + k];
135
- lo = (k == 1) ? lo2 : 0x80;
136
- hi = (k == 1) ? hi2 : 0xBF;
82
+ unsigned char cc = s[i + k];
83
+ unsigned char lo = k == 1 ? lo2 : 0x80;
84
+ unsigned char hi = k == 1 ? hi2 : 0xBF;
137
85
  if (cc < lo || cc > hi)
138
86
  return 0;
139
- cp = (cp << 6) | (cc & 0x3F);
140
87
  }
141
- (void)cp;
142
88
  i += n;
143
89
  }
144
90
  return 1;
145
91
  }
146
92
 
147
- mcdb_status mcdb_validate_header(const unsigned char *in, size_t in_len,
148
- uint64_t *out_original_size) {
149
- uint64_t original_size;
93
+ mcdb_status mcdb_parse_header(const unsigned char *in, size_t in_len, mcdb_header *out_header) {
94
+ mcdb_header header;
95
+ size_t max_envelope;
150
96
 
151
- if (in == NULL || in_len < MCDB_HEADER_SIZE)
97
+ if (in == NULL || in_len < MCDB_V1_HEADER_SIZE)
152
98
  return MCDB_ERR_TRUNCATED;
153
- if (in_len > MCDB_MAX_ENVELOPE)
154
- return MCDB_ERR_ENVELOPE_SIZE;
155
99
  if (in[0] != MCDB_MAGIC0 || in[1] != MCDB_MAGIC1 || in[2] != MCDB_MAGIC2 ||
156
100
  in[3] != MCDB_MAGIC3)
157
101
  return MCDB_ERR_MAGIC;
158
- if (in[4] != MCDB_VERSION)
102
+ if (in[4] != MCDB_VERSION_V1 && in[4] != MCDB_VERSION_V2)
159
103
  return MCDB_ERR_VERSION;
104
+
105
+ memset(&header, 0, sizeof(header));
106
+ header.version = in[4];
107
+ header.header_size =
108
+ header.version == MCDB_VERSION_V2 ? MCDB_V2_HEADER_SIZE : MCDB_V1_HEADER_SIZE;
109
+ max_envelope = header.version == MCDB_VERSION_V2 ? MCDB_MAX_ENVELOPE_V2 : MCDB_MAX_ENVELOPE_V1;
110
+
111
+ if (in_len < header.header_size)
112
+ return MCDB_ERR_TRUNCATED;
113
+ if (in_len > max_envelope)
114
+ return MCDB_ERR_ENVELOPE_SIZE;
160
115
  if (in[5] != MCDB_CODEC_ZSTD)
161
116
  return MCDB_ERR_CODEC;
162
- if (in[6] != MCDB_FLAGS_V1)
117
+ if (in[6] != MCDB_FLAGS_NONE)
163
118
  return MCDB_ERR_FLAGS;
164
119
 
165
- original_size = read_u64_le(in + 7);
166
- if (original_size > MCDB_MAX_OUTPUT)
120
+ header.original_size = read_u64_le(in + MCDB_ORIGINAL_SIZE_OFFSET);
121
+ if (header.original_size > MCDB_MAX_OUTPUT)
167
122
  return MCDB_ERR_SIZE_LIMIT;
168
123
 
124
+ if (header.version == MCDB_VERSION_V2) {
125
+ header.dictionary_ref = read_u64_le(in + MCDB_DICTIONARY_REF_OFFSET);
126
+ if (header.dictionary_ref == 0 || header.dictionary_ref > MCDB_MAX_DICTIONARY_REF)
127
+ return MCDB_ERR_DICTIONARY_REF;
128
+ }
129
+
130
+ if (out_header)
131
+ *out_header = header;
132
+ return MCDB_OK;
133
+ }
134
+
135
+ mcdb_status mcdb_validate_header(const unsigned char *in, size_t in_len,
136
+ uint64_t *out_original_size) {
137
+ mcdb_header header;
138
+ mcdb_status st = mcdb_parse_header(in, in_len, &header);
139
+ if (st != MCDB_OK)
140
+ return st;
141
+ if (header.version != MCDB_VERSION_V1)
142
+ return MCDB_ERR_VERSION;
169
143
  if (out_original_size)
170
- *out_original_size = original_size;
144
+ *out_original_size = header.original_size;
171
145
  return MCDB_OK;
172
146
  }
173
147
 
174
- static mcdb_status mcdb_decode_payload(const unsigned char *in, size_t in_len,
175
- uint64_t original_size, unsigned char *out,
176
- size_t out_capacity, size_t *out_len, char *errbuf) {
177
- const unsigned char *frame;
178
- size_t frame_len;
179
- size_t frame_size;
180
- unsigned long long frame_content_size;
181
- size_t produced;
182
- uint32_t expected_crc;
183
- uint32_t actual_crc;
148
+ mcdb_status mcdb_validate_v2_header(const unsigned char *in, size_t in_len,
149
+ uint64_t *out_original_size, uint64_t *out_dictionary_ref) {
150
+ mcdb_header header;
151
+ mcdb_status st = mcdb_parse_header(in, in_len, &header);
152
+ if (st != MCDB_OK)
153
+ return st;
154
+ if (header.version != MCDB_VERSION_V2)
155
+ return MCDB_ERR_VERSION;
156
+ if (out_original_size)
157
+ *out_original_size = header.original_size;
158
+ if (out_dictionary_ref)
159
+ *out_dictionary_ref = header.dictionary_ref;
160
+ return MCDB_OK;
161
+ }
184
162
 
185
- if (out_len)
186
- *out_len = 0;
163
+ mcdb_status mcdb_validate_dictionary(const unsigned char *dictionary, size_t dictionary_len,
164
+ uint32_t *out_zstd_dictionary_id) {
165
+ uint32_t dict_id;
166
+ if (dictionary == NULL || dictionary_len == 0 || dictionary_len > MCDB_MAX_DICTIONARY_BYTES)
167
+ return MCDB_ERR_DICTIONARY_SIZE;
168
+ dict_id = ZSTD_getDictID_fromDict(dictionary, dictionary_len);
169
+ if (dict_id == 0)
170
+ return MCDB_ERR_DICTIONARY_ID;
171
+ if (out_zstd_dictionary_id)
172
+ *out_zstd_dictionary_id = dict_id;
173
+ return MCDB_OK;
174
+ }
187
175
 
188
- if (out == NULL || out_capacity < (size_t)original_size) {
189
- if (errbuf)
190
- snprintf(errbuf, MCDB_ERRLEN, "MCDB: output buffer is too small");
191
- return MCDB_ERR_SIZE_MISMATCH;
176
+ uint32_t mcdb_zstd_frame_dictionary_id(const unsigned char *frame, size_t frame_len) {
177
+ if (frame == NULL || frame_len == 0)
178
+ return 0;
179
+ return ZSTD_getDictID_fromFrame(frame, frame_len);
180
+ }
181
+
182
+ uint64_t mcdb_dictionary_ref(const unsigned char *in, size_t in_len, mcdb_status *out_status) {
183
+ mcdb_header header;
184
+ mcdb_status st = mcdb_parse_header(in, in_len, &header);
185
+ if (out_status)
186
+ *out_status = st;
187
+ if (st != MCDB_OK || header.version != MCDB_VERSION_V2) {
188
+ if (out_status && st == MCDB_OK)
189
+ *out_status = MCDB_ERR_VERSION;
190
+ return 0;
192
191
  }
192
+ return header.dictionary_ref;
193
+ }
193
194
 
194
- frame = in + MCDB_HEADER_SIZE;
195
- frame_len = in_len - MCDB_HEADER_SIZE;
195
+ static mcdb_status mcdb_validate_frame(const unsigned char *in, size_t in_len,
196
+ const mcdb_header *header, const unsigned char **out_frame,
197
+ size_t *out_frame_len, char *errbuf) {
198
+ const unsigned char *frame = in + header->header_size;
199
+ size_t frame_len = in_len - header->header_size;
200
+ size_t frame_size;
201
+ unsigned long long content_size;
202
+
203
+ if (frame_len == 0) {
204
+ if (errbuf)
205
+ snprintf(errbuf, MCDB_ERRLEN, "MCDB: missing zstd frame");
206
+ return MCDB_ERR_DECOMPRESS;
207
+ }
196
208
  frame_size = ZSTD_findFrameCompressedSize(frame, frame_len);
197
209
  if (ZSTD_isError(frame_size)) {
198
210
  if (errbuf)
@@ -206,28 +218,31 @@ static mcdb_status mcdb_decode_payload(const unsigned char *in, size_t in_len,
206
218
  "MCDB: zstd frame has trailing bytes or concatenated frames");
207
219
  return MCDB_ERR_TRAILING_DATA;
208
220
  }
209
-
210
- frame_content_size = ZSTD_getFrameContentSize(frame, frame_len);
211
- if (frame_content_size == ZSTD_CONTENTSIZE_ERROR ||
212
- frame_content_size == ZSTD_CONTENTSIZE_UNKNOWN || frame_content_size != original_size) {
221
+ content_size = ZSTD_getFrameContentSize(frame, frame_len);
222
+ if (content_size == ZSTD_CONTENTSIZE_ERROR || content_size == ZSTD_CONTENTSIZE_UNKNOWN ||
223
+ content_size != header->original_size) {
213
224
  if (errbuf)
214
225
  snprintf(errbuf, MCDB_ERRLEN, "MCDB: zstd frame content size does not match header");
215
226
  return MCDB_ERR_FRAME_CONTENT_SIZE;
216
227
  }
228
+ if (out_frame)
229
+ *out_frame = frame;
230
+ if (out_frame_len)
231
+ *out_frame_len = frame_len;
232
+ return MCDB_OK;
233
+ }
217
234
 
218
- produced = ZSTD_decompress(out, (size_t)original_size, frame, frame_len);
219
- if (ZSTD_isError(produced)) {
220
- if (errbuf)
221
- snprintf(errbuf, MCDB_ERRLEN, "MCDB: zstd error: %s", ZSTD_getErrorName(produced));
222
- return MCDB_ERR_DECOMPRESS;
223
- }
224
- if (produced != original_size) {
235
+ static mcdb_status mcdb_finish_decoded(const unsigned char *in, const mcdb_header *header,
236
+ unsigned char *out, size_t produced, size_t *out_len,
237
+ char *errbuf) {
238
+ uint32_t expected_crc;
239
+ uint32_t actual_crc;
240
+ if (produced != header->original_size) {
225
241
  if (errbuf)
226
242
  snprintf(errbuf, MCDB_ERRLEN, "MCDB: size mismatch (header %llu, got %zu)",
227
- (unsigned long long)original_size, produced);
243
+ (unsigned long long)header->original_size, produced);
228
244
  return MCDB_ERR_SIZE_MISMATCH;
229
245
  }
230
-
231
246
  expected_crc = read_u32_le(in + MCDB_CRC_OFFSET);
232
247
  actual_crc = mcdb_crc32(out, produced);
233
248
  if (actual_crc != expected_crc) {
@@ -236,14 +251,12 @@ static mcdb_status mcdb_decode_payload(const unsigned char *in, size_t in_len,
236
251
  expected_crc, actual_crc);
237
252
  return MCDB_ERR_CRC;
238
253
  }
239
-
240
254
  if (!mcdb_is_valid_utf8(out, produced)) {
241
255
  if (errbuf)
242
256
  snprintf(errbuf, MCDB_ERRLEN,
243
257
  "MCDB: payload is not valid UTF-8 text or contains a NUL byte");
244
258
  return MCDB_ERR_UTF8;
245
259
  }
246
-
247
260
  if (out_len)
248
261
  *out_len = produced;
249
262
  return MCDB_OK;
@@ -251,51 +264,159 @@ static mcdb_status mcdb_decode_payload(const unsigned char *in, size_t in_len,
251
264
 
252
265
  mcdb_status mcdb_decode_into(const unsigned char *in, size_t in_len, unsigned char *out,
253
266
  size_t out_capacity, size_t *out_len, char *errbuf) {
254
- uint64_t original_size = 0;
267
+ mcdb_header header;
268
+ const unsigned char *frame;
269
+ size_t frame_len;
270
+ size_t produced;
271
+ mcdb_status st;
255
272
 
256
273
  if (out_len)
257
274
  *out_len = 0;
258
275
  if (errbuf)
259
276
  errbuf[0] = '\0';
260
-
261
- original_size = read_u64_le(in + 7);
262
- return mcdb_decode_payload(in, in_len, original_size, out, out_capacity, out_len, errbuf);
277
+ st = mcdb_parse_header(in, in_len, &header);
278
+ if (st != MCDB_OK)
279
+ return st;
280
+ if (header.version != MCDB_VERSION_V1)
281
+ return MCDB_ERR_VERSION;
282
+ if (out == NULL || out_capacity < header.original_size)
283
+ return MCDB_ERR_SIZE_MISMATCH;
284
+ st = mcdb_validate_frame(in, in_len, &header, &frame, &frame_len, errbuf);
285
+ if (st != MCDB_OK)
286
+ return st;
287
+ produced = ZSTD_decompress(out, (size_t)header.original_size, frame, frame_len);
288
+ if (ZSTD_isError(produced)) {
289
+ if (errbuf)
290
+ snprintf(errbuf, MCDB_ERRLEN, "MCDB: zstd error: %s", ZSTD_getErrorName(produced));
291
+ return MCDB_ERR_DECOMPRESS;
292
+ }
293
+ return mcdb_finish_decoded(in, &header, out, produced, out_len, errbuf);
263
294
  }
264
295
 
265
296
  mcdb_status mcdb_decode(const unsigned char *in, size_t in_len, unsigned char **out,
266
297
  size_t *out_len, char *errbuf) {
267
- uint64_t original_size = 0;
268
- mcdb_status st;
298
+ mcdb_header header;
269
299
  unsigned char *buf;
270
-
300
+ mcdb_status st;
271
301
  if (out)
272
302
  *out = NULL;
273
303
  if (out_len)
274
304
  *out_len = 0;
275
305
  if (errbuf)
276
306
  errbuf[0] = '\0';
277
-
278
- st = mcdb_validate_header(in, in_len, &original_size);
307
+ st = mcdb_validate_header(in, in_len, &header.original_size);
279
308
  if (st != MCDB_OK) {
280
309
  if (errbuf)
281
310
  snprintf(errbuf, MCDB_ERRLEN, "MCDB: %s", mcdb_status_str(st));
282
311
  return st;
283
312
  }
284
-
285
- buf = (unsigned char *)malloc(original_size ? original_size : 1);
313
+ buf = (unsigned char *)malloc(header.original_size ? (size_t)header.original_size : 1);
286
314
  if (buf == NULL) {
287
315
  if (errbuf)
288
316
  snprintf(errbuf, MCDB_ERRLEN, "MCDB: out of memory");
289
317
  return MCDB_ERR_ALLOC;
290
318
  }
291
-
292
- st =
293
- mcdb_decode_payload(in, in_len, original_size, buf, (size_t)original_size, out_len, errbuf);
319
+ st = mcdb_decode_into(in, in_len, buf, (size_t)header.original_size, out_len, errbuf);
294
320
  if (st != MCDB_OK) {
295
321
  free(buf);
296
322
  return st;
297
323
  }
324
+ if (out)
325
+ *out = buf;
326
+ else
327
+ free(buf);
328
+ return MCDB_OK;
329
+ }
330
+
331
+ mcdb_status mcdb_decode_v2_into(const unsigned char *in, size_t in_len,
332
+ uint64_t supplied_dictionary_ref, const unsigned char *dictionary,
333
+ size_t dictionary_len, ZSTD_DCtx *dctx, ZSTD_DDict *ddict,
334
+ unsigned char *out, size_t out_capacity, size_t *out_len,
335
+ char *errbuf) {
336
+ mcdb_header header;
337
+ const unsigned char *frame;
338
+ size_t frame_len;
339
+ size_t produced;
340
+ uint32_t dictionary_id;
341
+ uint32_t frame_dictionary_id;
342
+ mcdb_status st;
343
+ int own_dctx = 0;
344
+
345
+ if (out_len)
346
+ *out_len = 0;
347
+ if (errbuf)
348
+ errbuf[0] = '\0';
349
+ st = mcdb_parse_header(in, in_len, &header);
350
+ if (st != MCDB_OK)
351
+ return st;
352
+ if (header.version != MCDB_VERSION_V2)
353
+ return MCDB_ERR_VERSION;
354
+ if (supplied_dictionary_ref != header.dictionary_ref)
355
+ return MCDB_ERR_DICTIONARY_REF;
356
+ if (out == NULL || out_capacity < header.original_size)
357
+ return MCDB_ERR_SIZE_MISMATCH;
358
+ st = mcdb_validate_dictionary(dictionary, dictionary_len, &dictionary_id);
359
+ if (st != MCDB_OK)
360
+ return st;
361
+ st = mcdb_validate_frame(in, in_len, &header, &frame, &frame_len, errbuf);
362
+ if (st != MCDB_OK)
363
+ return st;
364
+ frame_dictionary_id = mcdb_zstd_frame_dictionary_id(frame, frame_len);
365
+ if (frame_dictionary_id == 0 || frame_dictionary_id != dictionary_id)
366
+ return MCDB_ERR_DICTIONARY_ID;
367
+ if (ddict == NULL)
368
+ return MCDB_ERR_DICTIONARY_REQUIRED;
369
+ if (dctx == NULL) {
370
+ dctx = ZSTD_createDCtx();
371
+ if (dctx == NULL)
372
+ return MCDB_ERR_ALLOC;
373
+ own_dctx = 1;
374
+ }
375
+ produced = ZSTD_decompress_usingDDict(dctx, out, (size_t)header.original_size, frame, frame_len,
376
+ ddict);
377
+ if (own_dctx)
378
+ ZSTD_freeDCtx(dctx);
379
+ if (ZSTD_isError(produced)) {
380
+ if (errbuf)
381
+ snprintf(errbuf, MCDB_ERRLEN, "MCDB: zstd dictionary error: %s",
382
+ ZSTD_getErrorName(produced));
383
+ return MCDB_ERR_DECOMPRESS;
384
+ }
385
+ return mcdb_finish_decoded(in, &header, out, produced, out_len, errbuf);
386
+ }
298
387
 
388
+ mcdb_status mcdb_decode_v2(const unsigned char *in, size_t in_len, uint64_t supplied_dictionary_ref,
389
+ const unsigned char *dictionary, size_t dictionary_len,
390
+ unsigned char **out, size_t *out_len, char *errbuf) {
391
+ mcdb_header header;
392
+ ZSTD_DDict *ddict;
393
+ mcdb_status st;
394
+ unsigned char *buf;
395
+ if (out)
396
+ *out = NULL;
397
+ if (out_len)
398
+ *out_len = 0;
399
+ st = mcdb_validate_v2_header(in, in_len, &header.original_size, NULL);
400
+ if (st != MCDB_OK)
401
+ return st;
402
+ st = mcdb_validate_dictionary(dictionary, dictionary_len, NULL);
403
+ if (st != MCDB_OK)
404
+ return st;
405
+ ddict = ZSTD_createDDict(dictionary, dictionary_len);
406
+ if (ddict == NULL)
407
+ return MCDB_ERR_ALLOC;
408
+ buf = (unsigned char *)malloc(header.original_size ? (size_t)header.original_size : 1);
409
+ if (buf == NULL) {
410
+ ZSTD_freeDDict(ddict);
411
+ return MCDB_ERR_ALLOC;
412
+ }
413
+ st = mcdb_decode_v2_into(in, in_len, supplied_dictionary_ref, dictionary, dictionary_len, NULL,
414
+ ddict, buf, (size_t)header.original_size, out_len, errbuf);
415
+ ZSTD_freeDDict(ddict);
416
+ if (st != MCDB_OK) {
417
+ free(buf);
418
+ return st;
419
+ }
299
420
  if (out)
300
421
  *out = buf;
301
422
  else
@@ -303,6 +424,130 @@ mcdb_status mcdb_decode(const unsigned char *in, size_t in_len, unsigned char **
303
424
  return MCDB_OK;
304
425
  }
305
426
 
427
+ /* Tiny SHA-256 implementation; dictionary registry validation must not depend on pgcrypto/OpenSSL.
428
+ */
429
+ typedef struct {
430
+ uint32_t state[8];
431
+ uint64_t bitlen;
432
+ unsigned char data[64];
433
+ size_t datalen;
434
+ } mcdb_sha256_ctx;
435
+
436
+ #define MCDB_ROTR32(x, n) (((x) >> (n)) | ((x) << (32u - (n))))
437
+ #define MCDB_CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
438
+ #define MCDB_MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
439
+ #define MCDB_EP0(x) (MCDB_ROTR32((x), 2) ^ MCDB_ROTR32((x), 13) ^ MCDB_ROTR32((x), 22))
440
+ #define MCDB_EP1(x) (MCDB_ROTR32((x), 6) ^ MCDB_ROTR32((x), 11) ^ MCDB_ROTR32((x), 25))
441
+ #define MCDB_SIG0(x) (MCDB_ROTR32((x), 7) ^ MCDB_ROTR32((x), 18) ^ ((x) >> 3))
442
+ #define MCDB_SIG1(x) (MCDB_ROTR32((x), 17) ^ MCDB_ROTR32((x), 19) ^ ((x) >> 10))
443
+
444
+ static const uint32_t MCDB_SHA256_K[64] = {
445
+ 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U,
446
+ 0xab1c5ed5U, 0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU,
447
+ 0x9bdc06a7U, 0xc19bf174U, 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU,
448
+ 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, 0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U,
449
+ 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U, 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU,
450
+ 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U, 0xa2bfe8a1U, 0xa81a664bU,
451
+ 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U, 0x19a4c116U,
452
+ 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
453
+ 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U,
454
+ 0xc67178f2U,
455
+ };
456
+
457
+ static void mcdb_sha256_transform(mcdb_sha256_ctx *ctx, const unsigned char data[64]) {
458
+ uint32_t m[64];
459
+ uint32_t a, b, c, d, e, f, g, h;
460
+ uint32_t t1, t2;
461
+ size_t i;
462
+ for (i = 0; i < 16; i++) {
463
+ m[i] = ((uint32_t)data[i * 4] << 24) | ((uint32_t)data[i * 4 + 1] << 16) |
464
+ ((uint32_t)data[i * 4 + 2] << 8) | (uint32_t)data[i * 4 + 3];
465
+ }
466
+ for (i = 16; i < 64; i++)
467
+ m[i] = MCDB_SIG1(m[i - 2]) + m[i - 7] + MCDB_SIG0(m[i - 15]) + m[i - 16];
468
+ a = ctx->state[0];
469
+ b = ctx->state[1];
470
+ c = ctx->state[2];
471
+ d = ctx->state[3];
472
+ e = ctx->state[4];
473
+ f = ctx->state[5];
474
+ g = ctx->state[6];
475
+ h = ctx->state[7];
476
+ for (i = 0; i < 64; i++) {
477
+ t1 = h + MCDB_EP1(e) + MCDB_CH(e, f, g) + MCDB_SHA256_K[i] + m[i];
478
+ t2 = MCDB_EP0(a) + MCDB_MAJ(a, b, c);
479
+ h = g;
480
+ g = f;
481
+ f = e;
482
+ e = d + t1;
483
+ d = c;
484
+ c = b;
485
+ b = a;
486
+ a = t1 + t2;
487
+ }
488
+ ctx->state[0] += a;
489
+ ctx->state[1] += b;
490
+ ctx->state[2] += c;
491
+ ctx->state[3] += d;
492
+ ctx->state[4] += e;
493
+ ctx->state[5] += f;
494
+ ctx->state[6] += g;
495
+ ctx->state[7] += h;
496
+ }
497
+
498
+ static void mcdb_sha256_init(mcdb_sha256_ctx *ctx) {
499
+ static const uint32_t initial[8] = {0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU,
500
+ 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U};
501
+ memcpy(ctx->state, initial, sizeof(initial));
502
+ ctx->bitlen = 0;
503
+ ctx->datalen = 0;
504
+ }
505
+
506
+ static void mcdb_sha256_update(mcdb_sha256_ctx *ctx, const unsigned char *data, size_t len) {
507
+ size_t i;
508
+ for (i = 0; i < len; i++) {
509
+ ctx->data[ctx->datalen++] = data[i];
510
+ if (ctx->datalen == 64) {
511
+ mcdb_sha256_transform(ctx, ctx->data);
512
+ ctx->bitlen += 512;
513
+ ctx->datalen = 0;
514
+ }
515
+ }
516
+ }
517
+
518
+ static void mcdb_sha256_final(mcdb_sha256_ctx *ctx, unsigned char out[MCDB_SHA256_BYTES]) {
519
+ size_t i;
520
+ uint64_t bitlen;
521
+ ctx->bitlen += (uint64_t)ctx->datalen * 8u;
522
+ ctx->data[ctx->datalen++] = 0x80;
523
+ if (ctx->datalen > 56) {
524
+ while (ctx->datalen < 64)
525
+ ctx->data[ctx->datalen++] = 0;
526
+ mcdb_sha256_transform(ctx, ctx->data);
527
+ ctx->datalen = 0;
528
+ }
529
+ while (ctx->datalen < 56)
530
+ ctx->data[ctx->datalen++] = 0;
531
+ bitlen = ctx->bitlen;
532
+ for (i = 0; i < 8; i++)
533
+ ctx->data[63 - i] = (unsigned char)(bitlen >> (8u * i));
534
+ mcdb_sha256_transform(ctx, ctx->data);
535
+ for (i = 0; i < 8; i++) {
536
+ out[i * 4] = (unsigned char)(ctx->state[i] >> 24);
537
+ out[i * 4 + 1] = (unsigned char)(ctx->state[i] >> 16);
538
+ out[i * 4 + 2] = (unsigned char)(ctx->state[i] >> 8);
539
+ out[i * 4 + 3] = (unsigned char)ctx->state[i];
540
+ }
541
+ }
542
+
543
+ void mcdb_sha256(const unsigned char *data, size_t len, unsigned char out[MCDB_SHA256_BYTES]) {
544
+ mcdb_sha256_ctx ctx;
545
+ mcdb_sha256_init(&ctx);
546
+ if (data != NULL && len > 0)
547
+ mcdb_sha256_update(&ctx, data, len);
548
+ mcdb_sha256_final(&ctx, out);
549
+ }
550
+
306
551
  const char *mcdb_status_str(mcdb_status s) {
307
552
  switch (s) {
308
553
  case MCDB_OK:
@@ -321,6 +566,14 @@ const char *mcdb_status_str(mcdb_status s) {
321
566
  return "reserved flags must be 0";
322
567
  case MCDB_ERR_SIZE_LIMIT:
323
568
  return "declared size over limit";
569
+ case MCDB_ERR_DICTIONARY_REQUIRED:
570
+ return "dictionary is required";
571
+ case MCDB_ERR_DICTIONARY_REF:
572
+ return "dictionary reference mismatch or invalid reference";
573
+ case MCDB_ERR_DICTIONARY_SIZE:
574
+ return "dictionary is empty or over limit";
575
+ case MCDB_ERR_DICTIONARY_ID:
576
+ return "zstd dictionary identifier mismatch or missing identifier";
324
577
  case MCDB_ERR_DECOMPRESS:
325
578
  return "corrupt zstd payload";
326
579
  case MCDB_ERR_TRAILING_DATA: