oinky 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +141 -0
  3. data/ext/extconf.rb +79 -0
  4. data/ext/include/oinky.h +424 -0
  5. data/ext/include/oinky.hpp +63 -0
  6. data/ext/include/oinky/nky_base.hpp +1116 -0
  7. data/ext/include/oinky/nky_core.hpp +1603 -0
  8. data/ext/include/oinky/nky_cursor.hpp +665 -0
  9. data/ext/include/oinky/nky_dialect.hpp +107 -0
  10. data/ext/include/oinky/nky_error.hpp +164 -0
  11. data/ext/include/oinky/nky_fixed_table.hpp +710 -0
  12. data/ext/include/oinky/nky_handle.hpp +334 -0
  13. data/ext/include/oinky/nky_index.hpp +1038 -0
  14. data/ext/include/oinky/nky_log.hpp +15 -0
  15. data/ext/include/oinky/nky_merge_itr.hpp +403 -0
  16. data/ext/include/oinky/nky_model.hpp +110 -0
  17. data/ext/include/oinky/nky_pool.hpp +760 -0
  18. data/ext/include/oinky/nky_public.hpp +808 -0
  19. data/ext/include/oinky/nky_serializer.hpp +1625 -0
  20. data/ext/include/oinky/nky_strtable.hpp +504 -0
  21. data/ext/include/oinky/nky_table.hpp +1996 -0
  22. data/ext/nky_lib.cpp +390 -0
  23. data/ext/nky_lib_core.hpp +212 -0
  24. data/ext/nky_lib_index.cpp +158 -0
  25. data/ext/nky_lib_table.cpp +224 -0
  26. data/lib/oinky.rb +1284 -0
  27. data/lib/oinky/compiler.rb +106 -0
  28. data/lib/oinky/cpp_emitter.rb +311 -0
  29. data/lib/oinky/dsl.rb +167 -0
  30. data/lib/oinky/error.rb +19 -0
  31. data/lib/oinky/modelbase.rb +12 -0
  32. data/lib/oinky/nbuffer.rb +152 -0
  33. data/lib/oinky/normalize.rb +132 -0
  34. data/lib/oinky/oc_builder.rb +44 -0
  35. data/lib/oinky/query.rb +193 -0
  36. data/lib/oinky/rb_emitter.rb +147 -0
  37. data/lib/oinky/shard.rb +40 -0
  38. data/lib/oinky/testsup.rb +104 -0
  39. data/lib/oinky/version.rb +9 -0
  40. data/oinky.gemspec +36 -0
  41. metadata +120 -0
@@ -0,0 +1,1625 @@
1
+ // This source is distributed under the terms of the MIT License. Refer
2
+ // to the 'LICENSE' file for details.
3
+ //
4
+ // Copyright (c) Jacob Lacouture, 2012
5
+
6
+ namespace Oinky
7
+ {
8
+ using namespace Oinky::Errors;
9
+ using namespace Oinky::Internal;
10
+
11
+ namespace Serialization
12
+ {
13
+
14
+ // NOTE: THIS IS OUT OF DATE, but it gives you the gist of it.
15
+ //
16
+ // <DB encoding string - 12 bytes> "\x0coinky-0.1.0"
17
+ // <DB length>
18
+ // <m_stringtable>
19
+ // uint32 byte_count; (including the byte_count itself)
20
+ // uint32 item_count;
21
+ // uint32 offsets[item_count];
22
+ // <bytes> strings </>
23
+ // /m_stringtable>
24
+ // <u_stringtable>
25
+ // uint32 byte_count;
26
+ // uint32 item_count;
27
+ // uint32 offsets[item_count];
28
+ // <bytes> strings </>
29
+ // /u_stringtable>
30
+ // <tableset
31
+ // <uint32 - total byte length of tableset>
32
+ // <uint32 - table count>
33
+ // <vector of m_strtable_references -- length==table_count>
34
+ // <vector of uint32 offsets (of beginning of each table. Offsets are from beginning
35
+ // of tableset) -- length==table_count>
36
+ // <table0 (name inherited from set header vector)
37
+ // <row_idx_t - row_count == the table and all indexes have the same number of rows
38
+ // <uint32 - row0_offset (offset of serialized rowdata, from beginningof <table
39
+ // <colset
40
+ // uint8 - column_count
41
+ // vector of column_def structures, which are fixed-length:
42
+ // [m_strtable_ref columnname,
43
+ // uint8 coltype,
44
+ // default_value (this is padded up to 8 bytes(maximum value size))]
45
+ // /colset>
46
+ // <indexset
47
+ // <uint32 - total byte length of indexset>
48
+ // <uint8 index_count
49
+ // <indexdef
50
+ // m_strtable_ref indexname
51
+ // uint32 - offset of tabledata, from beginning of <indexset>
52
+ // uint8 col_count
53
+ // bool unique
54
+ // vector of struct {
55
+ // uint8 col_ref (reference into table's colset)
56
+ // bool ascending
57
+ // }
58
+ // <index0 data
59
+ // array of row_idx_t references into table. length=table.row_count
60
+ // /data>
61
+ // <index1 data>
62
+ // /indexset>
63
+ // <table data>
64
+ // /table>
65
+ // <table1>
66
+ // ...
67
+ // /tableset>
68
+
69
+ namespace V1Defs
70
+ {
71
+ #pragma pack(push,1)
72
+ // We use machine-native serialization for these structures.
73
+ class s_table_hdr
74
+ {
75
+ struct layout_t {
76
+ m_strtable_ref tblname;
77
+ uint32 offset;
78
+ } s_value;
79
+
80
+ public:
81
+ typedef layout_t unpacked_t;
82
+
83
+ unpacked_t unpack() const
84
+ {
85
+ unpacked_t u;
86
+ unpack_nbc<v1_sformat_tag, m_strtable_ref>(&u.tblname, (const char *)&s_value.tblname);
87
+ unpack_nbc<v1_sformat_tag, uint32>(&u.offset, (const char *)&s_value.offset);
88
+ return u;
89
+ }
90
+
91
+ static void pack(m_strtable_ref tblname, uint32 offset, char *&write_cursor, const char *end_buf)
92
+ {
93
+ // Check length.
94
+ if (write_cursor + sizeof(layout_t) > end_buf) {
95
+ throw_error(buffer_overflow());
96
+ }
97
+ pack_nbc<v1_sformat_tag, m_strtable_ref>(tblname, write_cursor);
98
+ write_cursor += sizeof(m_strtable_ref);
99
+ pack_nbc<v1_sformat_tag, uint32>(offset, write_cursor);
100
+ write_cursor += 4;
101
+ }
102
+ };
103
+ BOOST_STATIC_ASSERT(sizeof(s_table_hdr) == 8);
104
+
105
+ class s_column_def
106
+ {
107
+ struct layout_t {
108
+ m_strtable_ref colname;
109
+ uint8 ctype;
110
+ uint8 vtype;
111
+ char default_value_bytes[8];
112
+ } s_value;
113
+
114
+ public:
115
+ typedef layout_t unpacked_t;
116
+
117
+ unpacked_t unpack() const
118
+ {
119
+ unpacked_t u = s_value;
120
+ // Only this requires further processing.
121
+ unpack_nbc<v1_sformat_tag, m_strtable_ref>(&u.colname, (const char *)&s_value.colname);
122
+ return u;
123
+ }
124
+
125
+ template<typename DB>
126
+ static void pack(const column_ctx &col, const DB *db, char *&write_cursor, const char *end_buf)
127
+ {
128
+ // Check length.
129
+ if (write_cursor + sizeof(layout_t) > end_buf) {
130
+ throw_error(buffer_overflow());
131
+ }
132
+ pack_nbc<v1_sformat_tag, m_strtable_ref>(db->metastrings.xform_ref(col.colname), write_cursor);
133
+ write_cursor += sizeof(m_strtable_ref);
134
+ pack_nbc<v1_sformat_tag, uint8>((uint8) col.ctype, write_cursor);
135
+ write_cursor += 1;
136
+ pack_nbc<v1_sformat_tag, uint8>((uint8) col.default_value.type(), write_cursor);
137
+ write_cursor += 1;
138
+ i_stored_value::pack_variant(col.default_value, write_cursor, db->userstrings);
139
+ write_cursor += 8;
140
+ }
141
+ };
142
+ BOOST_STATIC_ASSERT(sizeof(char) == 1);
143
+ BOOST_STATIC_ASSERT(sizeof(uint8) == 1);
144
+ BOOST_STATIC_ASSERT(sizeof(m_strtable_ref)==4);
145
+ BOOST_STATIC_ASSERT(sizeof(s_column_def) == sizeof(m_strtable_ref) + 10);
146
+
147
+ #pragma pack(pop)
148
+ } //namespace V1Defs
149
+ using namespace Oinky::Serialization::V1Defs;
150
+
151
+ // The scheme identifies itself.
152
+ template<>
153
+ class Serializer<v1_sformat_tag, v1_sformat_tag>
154
+ {
155
+ static const char *string() {
156
+ return "\x0coinky-0.1.1";
157
+ }
158
+ public:
159
+ static uint32 virtual_pack()
160
+ {
161
+ return strlen(string());
162
+ }
163
+
164
+ static void pack(char *&buffer_start, const char *buffer_end)
165
+ {
166
+ uint32 l = virtual_pack();
167
+ if (buffer_end - buffer_start < l) {
168
+ throw_error(buffer_overflow());
169
+ }
170
+ memcpy(buffer_start, string(), l);
171
+ buffer_start += l;
172
+ }
173
+
174
+ static void unpack(const char *buffer, uint32 buflen, uint32 last_end, uint32 &this_end)
175
+ {
176
+ if (!Serialization::check_bytes(string(), virtual_pack(), buffer, buflen, last_end, this_end)) {
177
+ throw_error(version_mismatch());
178
+ }
179
+ }
180
+ };
181
+ // The scheme identifies itself.
182
+ struct v1_trailer {};
183
+ template<>
184
+ class Serializer<v1_trailer, v1_sformat_tag>
185
+ {
186
+ static const char *string() {
187
+ return "oink";
188
+ }
189
+ public:
190
+ static uint32 virtual_pack() {
191
+ return strlen(string());
192
+ }
193
+
194
+ static void pack(char *&buffer_start, const char *buffer_end)
195
+ {
196
+ uint32 l = virtual_pack();
197
+ if (buffer_end - buffer_start < l) {
198
+ throw_error(buffer_overflow());
199
+ }
200
+ memcpy(buffer_start, string(), l);
201
+ buffer_start += l;
202
+ }
203
+
204
+ static void unpack(const char *buffer, uint32 buflen, uint32 last_end, uint32 &this_end)
205
+ {
206
+ if (!Serialization::check_bytes(string(), virtual_pack(), buffer, buflen, last_end, this_end)) {
207
+ throw_error(version_mismatch());
208
+ }
209
+ }
210
+ };
211
+
212
+ template<typename ALLOCATOR>
213
+ class Serializer<db_handle_internal_t<ALLOCATOR>, v1_sformat_tag>
214
+ {
215
+ typedef v1_sformat_tag scheme;
216
+ typedef db_handle_internal_t<ALLOCATOR> db_handle_internal;
217
+ typedef typename db_handle_internal::table_ctx table_ctx;
218
+ typedef typename table_ctx::index_ctx index_ctx;
219
+ typedef typename index_ctx::idx_column_iterator idx_column_iterator;
220
+
221
+ //#############################################################################
222
+ //##
223
+ //## DESERIALIZE
224
+ //##
225
+ //#############################################################################
226
+
227
+ template<typename s_value, typename CTX_TRAITS, typename MAP_FN>
228
+ static void unpack_set(context_map_t<CTX_TRAITS> *map, const MAP_FN &mapfn, const char *buffer, uint32 buflen, uint32 last_end, uint32 &this_end)
229
+ {
230
+ typedef typename CTX_TRAITS::index_t index_t;
231
+ typedef typename CTX_TRAITS::context_t context_t;
232
+ index_t count;
233
+ Serialization::unpack<scheme>(&count, buffer, buflen, this_end, this_end);
234
+
235
+ if (((uint32) count > buflen - this_end) ||
236
+ (((uint32) count) * sizeof(s_value) > buflen - this_end))
237
+ {
238
+ // This is not a buffer underflow. That can happen only at the
239
+ // top-level. The size of this table was itself encoded, so if
240
+ // it's wrong, that counts as a bad encoding.
241
+ throw_error(bad_encoding());
242
+ }
243
+ const s_value *s_cols = (const s_value *)(buffer + this_end);
244
+ this_end += count * sizeof(s_value);
245
+ // TBD: Something like the column contexts could just be used in their
246
+ // serialized form. There's no need to allocate new contexts and
247
+ // extract them, but this is a bit simpler (static and dynamic
248
+ // contexts can be treated the same.) This is slightly less efficient
249
+ // (since we have another allocation for every table. Also, the
250
+ // table structures will rarely be modified relative to the number of
251
+ // times the DB is mounted in its lifetime, so it's the last thing that
252
+ // should be optimized for speed. For now, this is optimized for simplicity.
253
+
254
+ map->f_contexts.resize(count);
255
+ map->_live_count = count;
256
+
257
+ const s_value *sc = s_cols;
258
+ context_t *ctx = map->f_contexts.begin();
259
+ for (index_t i=(index_t)0;i<count;++i, ++ctx, ++sc) {
260
+ mapfn(i, ctx, sc);
261
+ };
262
+ }
263
+
264
+ static void unpack_col_ctx(row_offset_accumulator_t *row_width, column_idx_t i, column_ctx *colctx, const s_column_def *s_coldef, table_ctx *table)
265
+ {
266
+ const s_column_def::unpacked_t sc(s_coldef->unpack());
267
+
268
+ colctx->colname = table->db->metastrings.from_untrusted_ref(sc.colname);
269
+ colctx->ctype = (column_type_code_t) sc.ctype;
270
+ //colctx->position = i;
271
+
272
+ // Check the column type.
273
+ if (sc.ctype > OINKY_VALUE_TYPE_MAX) {
274
+ throw_error(bad_encoding());
275
+ }
276
+
277
+ // Determine where these column values are stored in the fixed row.
278
+ if (colctx->ctype == column_types::Bit) {
279
+ // we use new_bit_offset as the temporary because it can hold the
280
+ // larger value that frow_bit_offset.
281
+ colctx->new_bit_offset = row_width->bit_count;
282
+ row_width->bit_count += 1;
283
+ } else {
284
+ colctx->frow_byte_offset = row_width->byte_offset;
285
+ // This will throw if the column_type is invalid
286
+ row_width->byte_offset += compute_serialized_width(colctx->ctype);
287
+ }
288
+
289
+ // Now determine where to store the column values in any pending
290
+ // rows we may create.
291
+ table->allocate_pending_column_data(colctx);
292
+
293
+ // Check the value type. A value cannot be variant.
294
+ if ((sc.vtype < (uint8) value_types::Bit) || (sc.vtype > OINKY_VALUE_TYPE_MAX)) {
295
+ throw_error(bad_encoding());
296
+ }
297
+ // Unpack the default value.
298
+ i_stored_value::unpack(&colctx->default_value, (value_type_code_t) sc.vtype, sc.default_value_bytes, &table->db->userstrings);
299
+ }
300
+
301
+ static void unpack_index_ctx(table_ctx *table, index_ctx *idx, index_idx_t _index_idx, const char *buffer, uint32 buflen, uint32 last_end, uint32 &this_end)
302
+ {
303
+ idx->reinit_serial(table, _index_idx);
304
+
305
+ // How many columns in this index.
306
+ m_strtable_ref idxnameref;
307
+ uint32 rowdata_offset;
308
+ column_idx_t col_count;
309
+ bool require_unique;
310
+
311
+ this_end = last_end;
312
+
313
+ Serialization::unpack<scheme>(&idxnameref, buffer, buflen, this_end, this_end);
314
+ Serialization::unpack<scheme>(&rowdata_offset, buffer, buflen, this_end, this_end);
315
+ Serialization::unpack<scheme>(&col_count, buffer, buflen, this_end, this_end);
316
+ Serialization::unpack<scheme>(&require_unique, buffer, buflen, this_end, this_end);
317
+
318
+ idx->indexname = table->db->metastrings.from_untrusted_ref(idxnameref);
319
+
320
+ // Extract the index column definition data.
321
+ uint32 remainder = buflen - this_end;
322
+ uint32 ascending_bytes = (col_count + 7) >> 3;
323
+ if ((col_count * sizeof(column_idx_t)) + ascending_bytes > remainder) {
324
+ throw_error(bad_encoding());
325
+ }
326
+
327
+ idx->defn.col_count = col_count;
328
+ const column_idx_t *column_idx_refs = (const column_idx_t *) (buffer + this_end);
329
+ this_end += col_count * sizeof(column_idx_t);
330
+ idx->defn.cols_ascending = (const uint8 *) (buffer + this_end);
331
+ this_end += ascending_bytes;
332
+ idx->defn.require_unique = require_unique;
333
+
334
+ // Convert the index refs into column_ctx * refs.
335
+ {
336
+ idx->__definition_data.resize(sizeof(column_ctx *) * idx->defn.col_count);
337
+ const column_ctx **cols = (const column_ctx **) idx->__definition_data.begin();
338
+ idx->column_refs = cols;
339
+ for (uint32 i=0;i<(uint32) idx->defn.col_count;++i) {
340
+ column_idx_t val;
341
+ unpack_nbc<scheme,column_idx_t>(&val, (const char *)(column_idx_refs + i));
342
+ // Bounds check the column reference.
343
+ if ((uint32) val >= table->cols_by_position.size()) {
344
+ throw_error(bad_encoding());
345
+ }
346
+ cols[i] = table->cols_by_position[val];
347
+ }
348
+ }
349
+
350
+ // An index does not shadow the data itself. It merely provides an
351
+ // alternate ordering. Each row of an index is simply the row-id.
352
+ uint32 row_count = table->fixed.tabular.row_count;
353
+ index_rowkey_xform keyxform((row_idx_t) table->fixed.tabular.row_count);
354
+ uint32 row_width = keyxform.dr_width;
355
+
356
+ // Check that we have space for the offset list.
357
+ if ((rowdata_offset < this_end) ||
358
+ (rowdata_offset > buflen) ||
359
+ (row_count > buflen) ||
360
+ ((uint64) row_count * row_width > buflen - rowdata_offset))
361
+ {
362
+ throw_error(bad_encoding());
363
+ }
364
+
365
+ #if 0
366
+ //We do this in the fixed-index iterator itself. We don't need to do it here too.
367
+ // Check that none of the offsets exceeds the table size.
368
+ const row_idx_t *indexlist = (const row_idx_t *) (buffer + rowdata_offset);
369
+ if (array_exceeds_max_31(indexlist, row_count, (row_idx_t) (row_count - 1))) {
370
+ throw_error(bad_encoding());
371
+ }
372
+ #endif
373
+
374
+ // Extract the column value data.
375
+ typedef typename index_ctx::fixed_t tf_t;
376
+ idx->fixed = tf_t(
377
+ tabular_t(
378
+ (row_idx_t) row_count,
379
+ row_width,
380
+ buffer + rowdata_offset),
381
+ table,
382
+ idx);
383
+
384
+ // Check that all referenced columns are of indexable type. If they
385
+ // are not (are of variant type) then this is a bad encoding.
386
+ idx_column_iterator i = idx->coldefs_begin();
387
+ idx_column_iterator end = idx->coldefs_end();
388
+ for (;i != end; ++i) {
389
+ if (i->column->ctype == column_types::Variant) {
390
+ throw_error(bad_encoding());
391
+ }
392
+ }
393
+
394
+ // We do not advance this_end past the index data. We were only asked
395
+ // to unpack the context. The contexts are packed together.
396
+ }
397
+
398
+ // This uses int32s to test, meaning it can only test an upper limit up
399
+ // to 2^31-1.
400
+ template<typename INT_TYPE>
401
+ static bool array_exceeds_max_31(const INT_TYPE *array, uint32 count, INT_TYPE max) {
402
+ BOOST_STATIC_ASSERT(sizeof(INT_TYPE) <= sizeof(uint32));
403
+ if ((uint32) max >= (uint32) (1 << 31)) {
404
+ throw_error(invalid_argument());
405
+ }
406
+ const INT_TYPE *i = array;
407
+ const INT_TYPE *end = i + count;
408
+ int32 a = 0;
409
+ int32 maxrow = (int32) max;
410
+ for (;i != end; ++i) {
411
+ // This should be a free conversion. Casting to familiar integer
412
+ // types makes this clearer.
413
+ uint32 off = (uint32) *i;
414
+ // We only care if the high bit ever gets set.
415
+ a |= maxrow - off;
416
+ }
417
+ // If the high bit is set, then at least one element exceeded max.
418
+ return a < 0;
419
+ }
420
+
421
+ static void unpack_index_set(table_ctx *table, const char *buffer, uint32 buflen, uint32 last_end, uint32 &this_end)
422
+ {
423
+ this_end = last_end;
424
+ index_idx_t count;
425
+ Serialization::unpack<scheme>(&count, buffer, buflen, this_end, this_end);
426
+
427
+ // Unpack each index context.
428
+ table->indexes.f_contexts.resize(count);
429
+ table->indexes._live_count = count;
430
+
431
+ index_ctx *ic = table->indexes.f_contexts.begin();
432
+ for (index_idx_t i = (index_idx_t)0;i<count;++i, ++ic) {
433
+ unpack_index_ctx(table, ic, i, buffer, buflen, this_end, this_end);
434
+ }
435
+
436
+ table->indexes_by_position.assign_sequential(
437
+ table->indexes.f_contexts.begin(),
438
+ table->indexes.f_contexts.end());
439
+ }
440
+
441
+ static void unpack_table(
442
+ uint32 __tbl_idx, //ignored. tables never referenced by position.
443
+ table_ctx *table,
444
+ const s_table_hdr *s_hdr_data,
445
+ db_handle_internal *db,
446
+ const char *buffer, // Points to the beginning of the tableset.
447
+ uint32 buflen // Size of the tableset
448
+ )
449
+ {
450
+ const s_table_hdr::unpacked_t hdr_data(s_hdr_data->unpack());
451
+
452
+ // Clean slate.
453
+ table->reinit(db);
454
+ // Get the table name
455
+ table->tablename = db->metastrings.from_untrusted_ref(hdr_data.tblname);
456
+
457
+ // Move the beginning of this table spec.
458
+ if (hdr_data.offset >= buflen) {
459
+ throw_error(bad_encoding());
460
+ }
461
+ uint32 this_end = hdr_data.offset;
462
+
463
+ row_idx_t row_count;
464
+ uint32 rowdata_offset;
465
+ Serialization::unpack<scheme>(&row_count, buffer, buflen, this_end, this_end);
466
+ Serialization::unpack<scheme>(&rowdata_offset, buffer, buflen, this_end, this_end);
467
+
468
+ // Get the column data
469
+ row_offset_accumulator_t row_width;
470
+ unpack_set<s_column_def>(
471
+ &table->columns,
472
+ boost::bind(&unpack_col_ctx, &row_width, _1, _2, _3, table),
473
+ buffer,
474
+ buflen,
475
+ this_end,
476
+ this_end);
477
+
478
+ // Initialize the positional map.
479
+ uint32 column_count = table->columns.f_contexts.size();
480
+ table->cols_by_position.resize(column_count);
481
+ column_ctx **target_col_ptr = table->cols_by_position.begin();
482
+ column_ctx **end_col_ptr = table->cols_by_position.end();
483
+
484
+ for(column_ctx *col = table->columns.f_contexts.begin();
485
+ target_col_ptr != end_col_ptr;
486
+ ++col, ++target_col_ptr)
487
+ {
488
+ *target_col_ptr = col;
489
+
490
+ // Also update the bit/offset information if this is a bit column,
491
+ // now that we know the row length.
492
+ if (col->ctype == column_types::Bit) {
493
+ col->frow_byte_offset = row_width.byte_offset + (col->new_bit_offset >> 3);
494
+ col->frow_bit_offset = col->new_bit_offset & 7;
495
+ }
496
+ }
497
+
498
+ // Compute the actual row width, including bit columns.
499
+ uint32 serialized_row_width = row_width.full_width();
500
+
501
+ // Now the indexes. Unlike the column set, the index set is framed.
502
+ // Unpack the index set framing info first.
503
+ uint32 index_set_bytes;
504
+ Serialization::unpack<scheme>(&index_set_bytes, buffer, buflen, this_end, this_end);
505
+ uint32 ixsetbase = this_end - 4;
506
+ if (ixsetbase + index_set_bytes > buflen) {
507
+ throw_error(bad_encoding());
508
+ }
509
+
510
+ // Now unpack the row data (since we need to initialize the table's
511
+ // fixed object prior to unpacking the indexes.
512
+ //
513
+ // The serialized row width has been computed above.
514
+
515
+ // Extract the column value data.
516
+ if ((rowdata_offset < this_end) ||
517
+ (rowdata_offset > buflen) ||
518
+ ((uint64) row_count * serialized_row_width > buflen - rowdata_offset))
519
+ {
520
+ throw_error(bad_encoding());
521
+ }
522
+
523
+ // The rows get deserialized lazily. We just save a pointer to them.
524
+ typedef typename table_ctx::fixed_t tf_t;
525
+ table->fixed = tf_t(
526
+ tabular_t(
527
+ (row_idx_t) row_count,
528
+ serialized_row_width,
529
+ buffer + rowdata_offset),
530
+ table);
531
+
532
+ // Any new rows should have sequence numbers beginning beyond those
533
+ // reserved for the fixed rows.
534
+ table->insert_sequence_limit = (uint64) row_count;
535
+
536
+ // Now we can unpack the index set.
537
+ // Start at 4 bytes, since we already read the byte_count value.
538
+ uint32 ixtmp = 4;
539
+ unpack_index_set(
540
+ table,
541
+ buffer + ixsetbase,
542
+ index_set_bytes,
543
+ ixtmp,
544
+ ixtmp);
545
+
546
+ // Now advance the pointer beyond the frame, regardless of how much the
547
+ // ixtmp reports consuming. Note this is an assert not an exception.
548
+ // Even malicious encoding should not be able to provoke this.
549
+ OINKY_ASSERT(ixtmp <= index_set_bytes);
550
+ }
551
+
552
+ template<typename REF_T>
553
+ static void unpack_stringtable(
554
+ ALLOCATOR *allocator,
555
+ stringtable_t<REF_T> *target,
556
+ const char *buffer,
557
+ uint32 buflen,
558
+ uint32 last_end,
559
+ uint32 &this_end)
560
+ {
561
+ uint32 byte_count, string_count;
562
+ //
563
+ this_end = last_end;
564
+ Serialization::unpack<scheme>(&byte_count, buffer, buflen, this_end, this_end);
565
+ Serialization::unpack<scheme>(&string_count, buffer, buflen, this_end, this_end);
566
+ //
567
+ if (byte_count > buflen - last_end) {
568
+ throw_error(buffer_underflow());
569
+ }
570
+ // Test that we have space for the arrays.
571
+ if ((string_count > buflen) ||
572
+ (string_count * 4 > buflen - this_end)) {
573
+ throw_error(bad_encoding());
574
+ }
575
+ // These are not necessarily yet valid uint32s. We haven't byte
576
+ // reversed them yet (if necessary). That happens lazily in
577
+ // stringtable_t::from_trusted_ref.
578
+ const uint32 *offsets = (const uint32 *)(buffer + this_end);
579
+ this_end += string_count * 4;
580
+ #if 0
581
+ // TBD: The following is a very fast validation. It takes a few
582
+ // microseconds even for thousands of strings. However, if we need
583
+ // to byte-swap, it's worse. So we just do it lazily in
584
+ // stringtable_t::from_trusted_ref
585
+ const uint32 *i = offsets;
586
+ const uint32 *end = offsets + string_count;
587
+ uint32 max = byte_count;
588
+ uint32 min = this_end - last_end;
589
+ uint32 a = 0;
590
+ for (;i != end;++i) {
591
+ uint32 off = *i;
592
+ a |= off - min;
593
+ min = off;
594
+ }
595
+ // If any offset is greater than its subsequent offset.
596
+ if (a & (1<<31)) {
597
+ throw_error(bad_encoding());
598
+ }
599
+ // If the final is greater than the size of the table.
600
+ if (min > byte_count) {
601
+ throw_error(bad_encoding());
602
+ }
603
+ #endif
604
+ // Assign.
605
+ target->reinit(allocator, string_count, offsets, byte_count, buffer + last_end);
606
+ // Complete
607
+ this_end = last_end + byte_count;
608
+ }
609
+
610
+ static void unpack_tableset(
611
+ typename db_handle_internal::tables_t *target,
612
+ db_handle_internal *db,
613
+ const char *buffer,
614
+ uint32 buflen,
615
+ uint32 last_end,
616
+ uint32 &this_end)
617
+ {
618
+ uint32 byte_count;
619
+ //
620
+ Serialization::unpack<scheme>(&byte_count, buffer, buflen, last_end, this_end);
621
+ //
622
+ if (byte_count > buflen - last_end) {
623
+ throw_error(buffer_underflow());
624
+ }
625
+ // For frame size.
626
+ uint32 tbstmp = 4;
627
+ unpack_set<s_table_hdr>(
628
+ &db->tables,
629
+ boost::bind(&unpack_table, _1, _2, _3, db, buffer + last_end, byte_count),
630
+ buffer + last_end,
631
+ byte_count,
632
+ tbstmp,
633
+ tbstmp);
634
+
635
+ // Complete
636
+ this_end = last_end + byte_count;
637
+ }
638
+ public:
639
+
640
+ static void mount(db_handle_internal *target, const char *buffer, uint32 buflen) {
641
+ // Clean start.
642
+ target->reset();
643
+
644
+ try {
645
+ uint32 this_end = 0;
646
+
647
+ // Check the encoding.
648
+ // This will throw if the scheme is wrong.
649
+ Serializer<scheme,scheme>::unpack(buffer, buflen, this_end, this_end);
650
+
651
+ // Get the length (including the encoding string).
652
+ uint32 total_length;
653
+ Serialization::unpack<scheme>(&total_length, buffer, buflen, this_end, this_end);
654
+ if (total_length > buflen) {
655
+ throw_error(buffer_underflow());
656
+ }
657
+ // Unpack the meta-string table.
658
+ //
659
+ // The relational tables depend on the metastring table for their
660
+ // table/column/index names, and on the userstring table for the
661
+ // default-column-value definitions.
662
+ unpack_stringtable(&target->allocator, &target->metastrings, buffer, buflen, this_end, this_end);
663
+ // Unpack the user-string table.
664
+ unpack_stringtable(&target->allocator, &target->userstrings, buffer, buflen, this_end, this_end);
665
+ // Unpack the relational tables.
666
+ unpack_tableset(&target->tables, target, buffer, buflen, this_end, this_end);
667
+ } catch (...) {
668
+ // Clean exit.
669
+ target->reset();
670
+ throw;
671
+ }
672
+ }
673
+
674
+ //#############################################################################
675
+ //##
676
+ //## SERIALIZE
677
+ //##
678
+ //#############################################################################
679
+ typedef typename table_ctx::cols_positional_itr_t cols_itr_t;
680
+
681
+ static uint32 prepare_pack_index_hdr(index_ctx *index)
682
+ {
683
+ // Mark the index name.
684
+ index->table->db->metastrings.mark(index->indexname);
685
+ // The size of the index definition.
686
+ uint32 bytes = 0;
687
+ bytes += Serializer<m_strtable_ref,scheme>::virtual_pack();//index->idxname
688
+ bytes += Serializer<uint32,scheme>::virtual_pack();//rowdata_offset
689
+ bytes += Serializer<column_idx_t,scheme>::virtual_pack(index->defn.col_count);
690
+ bytes += Serializer<bool,scheme>::virtual_pack(index->defn.require_unique);
691
+ bytes += sizeof(column_idx_t) * index->defn.col_count;
692
+ // A bit per column to record sort-order.
693
+ uint32 ascending_bytes = (index->defn.col_count + 7) >> 3;
694
+ bytes += ascending_bytes;
695
+ return bytes;
696
+ }
697
+ static uint32 prepare_pack_index_data(index_ctx *index)
698
+ {
699
+ // The size of the index definition. The index name has already
700
+ // been accounted for by the context_t serializer.
701
+ index_rowkey_xform keyxform((row_idx_t) index->table->row_count());
702
+ return index->table->row_count() * keyxform.dr_width;
703
+ }
704
+ static void complete_pack_index(
705
+ index_ctx *index,
706
+ char *&hcursor,
707
+ char *idxset_base,
708
+ const char *end_buf,
709
+ uint32 *last_data_offset,
710
+ const row_idx_t *remap_vector)
711
+ {
712
+ //###############
713
+ // Write the index definition.
714
+ Serializer<m_strtable_ref,scheme>::pack(index->table->db->metastrings.xform_ref(index->indexname), hcursor, end_buf);
715
+ Serializer<uint32,scheme>::pack(*last_data_offset, hcursor, end_buf);
716
+ Serializer<column_idx_t,scheme>::pack(index->defn.col_count, hcursor, end_buf);
717
+ Serializer<bool,scheme>::pack(index->defn.require_unique, hcursor, end_buf);
718
+ idx_column_iterator coldefs_end = index->coldefs_end();
719
+ for (idx_column_iterator i = index->coldefs_begin(); i != coldefs_end; ++i) {
720
+ // The new position may be different from the temporary position
721
+ // we've been using internally.
722
+ Serializer<column_idx_t,scheme>::pack(
723
+ i->column->new_position,
724
+ hcursor,
725
+ end_buf);
726
+ }
727
+
728
+ // Pack the directional bits
729
+ uint32 ascending_bytes = (index->defn.col_count + 7) >> 3;
730
+ char *ascending_bytes_target = hcursor;
731
+ memset(ascending_bytes_target, 0, ascending_bytes);
732
+ uint32 pos = 0;
733
+ for (idx_column_iterator i = index->coldefs_begin(); i != coldefs_end; ++i, ++pos) {
734
+ // The new position may be different from the temporary position
735
+ // we've been using internally.
736
+ if (i->ascending) {
737
+ ascending_bytes_target[pos >> 3] |= 1 << (pos & 7);
738
+ }
739
+ }
740
+ hcursor += ascending_bytes;
741
+
742
+ //###############
743
+ // Rowdata.
744
+ char *rowrefcursor = idxset_base + *last_data_offset;
745
+ uint32 rowcount = index->table->row_count();
746
+ index_rowkey_xform keyxform((row_idx_t) rowcount);
747
+ uint32 index_data_bytes = rowcount * keyxform.dr_width;
748
+ if (rowrefcursor + index_data_bytes > end_buf) {
749
+ throw_error(buffer_overflow());
750
+ }
751
+
752
+ struct keyfn {
753
+ static inline row_idx_t newkey(typename index_ctx::iterator ri, const row_idx_t *remap_vector) {
754
+ if (ri.fixed_active()) {
755
+ OINKY_ASSERT(!ri.pending_active());
756
+ row_idx_t oldkey = *ri.fixed();
757
+ return remap_vector ? remap_vector[oldkey] : oldkey;
758
+ } else {
759
+ OINKY_ASSERT(ri.pending_active());
760
+ return ri.pending().to_row()->new_rowkey;
761
+ }
762
+ }
763
+ };
764
+
765
+ typename index_ctx::iterator ri = index->begin();
766
+ typename index_ctx::iterator re = index->end();
767
+
768
+ // We will do this in two passes. The first will be destructive to
769
+ // bits beyond those written by the cursor. At the end of the array,
770
+ // of course, we need to be careful not to overwrite anything, so
771
+ // we'll zero the bits we care about and or-in the last few values.
772
+ uint32 i = 4;
773
+ for (;i < rowcount; ++ri, ++i) {
774
+ row_idx_t newkey = keyfn::newkey(ri, remap_vector);
775
+ keyxform.pack_nbc_destructive<scheme>(rowrefcursor, newkey);
776
+ rowrefcursor += keyxform.dr_width;
777
+ }
778
+ i -= 4;
779
+ // Now zero the remainder of the target bytes.
780
+ if (i < rowcount) {
781
+ memset(rowrefcursor, 0, (rowcount - i) * keyxform.dr_width);
782
+ for (;ri != re; ++ri) {
783
+ row_idx_t newkey = keyfn::newkey(ri, remap_vector);
784
+ keyxform.pack_nbc_orbits<scheme>(rowrefcursor, newkey);
785
+ rowrefcursor += keyxform.dr_width;
786
+ }
787
+ }
788
+ OINKY_ASSERT(rowrefcursor ==
789
+ idxset_base + *last_data_offset + index_data_bytes);
790
+
791
+ // Remember the data cursor position.
792
+ *last_data_offset = rowrefcursor - idxset_base;
793
+ }
794
+
795
+ static uint32 prepare_pack_column(column_ctx *col, column_idx_t *new_column_idx, row_offset_accumulator_t *row_width, db_handle_internal *db)
796
+ {
797
+ // If the default value is a string, then mark it.
798
+ if (col->default_value.is_string()) {
799
+ db->userstrings.mark(col->default_value);
800
+ }
801
+
802
+ // Assign new column position.
803
+ col->new_position = *new_column_idx;
804
+ *new_column_idx += 1;
805
+ // Compute data-width.
806
+ if (col->ctype == column_types::Bit) {
807
+ col->new_bit_offset = row_width->bit_count;
808
+ row_width->bit_count += 1;
809
+ } else {
810
+ col->new_row_offset_bytes = row_width->byte_offset;
811
+ row_width->byte_offset += compute_serialized_width(col->ctype);
812
+ }
813
+ // The column object contains no payload. The header is the definition.
814
+ // The definition is already accounted for. 0 bytes for payload.
815
+ return 0;
816
+ }
817
+
818
+ static void complete_pack_column(
819
+ const column_ctx *col,
820
+ char *&write_cursor,
821
+ db_handle_internal *db,
822
+ const char *end_buf)
823
+ {
824
+ s_column_def::pack(*col, db, write_cursor, end_buf);
825
+ }
826
+
827
+ static uint32 prepare_pack_table(table_ctx *table, db_handle_internal *db)
828
+ {
829
+ uint32 bytes = 0;
830
+
831
+ if (must_remap_fixedkeys(table) &&
832
+ (db->packing.max_fixed_table_remap_size < (uint32) table->fixed.tabular.row_count))
833
+ {
834
+ db->packing.max_fixed_table_remap_size = (uint32) table->fixed.tabular.row_count;
835
+ }
836
+
837
+ bytes += Serializer<row_idx_t, scheme>::virtual_pack(); //row_count
838
+ bytes += Serializer<uint32, scheme>::virtual_pack(); //rowdata_offset
839
+
840
+ // Get the column data
841
+ column_idx_t new_column_position = (column_idx_t) 0;
842
+ table->packing.row_width = row_offset_accumulator_t();
843
+
844
+ bytes += prepare_pack_set<s_column_def>(
845
+ table->columns,
846
+ boost::bind(&prepare_pack_column, _1, &new_column_position, &table->packing.row_width, db));
847
+
848
+ // Compute the actual row width, including bit columns.
849
+ uint32 serialized_row_width = table->packing.row_width.full_width();
850
+
851
+ // Now iterate all the bit columns and reset their location.
852
+ cols_itr_t ci = table->cols_by_position.begin();
853
+ cols_itr_t endcols = table->cols_by_position.end();
854
+ for (;ci != endcols;++ci) {
855
+ column_ctx *col(*ci);
856
+ if (col->ctype == column_types::Bit) {
857
+ col->new_row_offset_bytes = table->packing.row_width.byte_offset + (col->new_bit_offset >> 3);
858
+ col->new_bit_offset &= 7;
859
+ }
860
+ }
861
+
862
+ //#########
863
+ // Index set header.
864
+ uint32 ixsetbytes = 0;
865
+ ixsetbytes += Serializer<uint32, scheme>::virtual_pack(); //index_set_bytes
866
+ uint32 ixhdrbytes = sizeof(index_idx_t); //index count
867
+
868
+ // Each index
869
+ typedef typename table_ctx::indexes_t::by_name_iterator ibni;
870
+ ibni ic = table->indexes.by_name().begin();
871
+ ibni iend = table->indexes.by_name().end();
872
+ for (;ic != iend; ++ic) {
873
+ ixhdrbytes += prepare_pack_index_hdr(*ic);
874
+ ixsetbytes += prepare_pack_index_data(*ic);
875
+ }
876
+ table->indexes.computed_packed_header_bytes = ixhdrbytes;
877
+ ixsetbytes += ixhdrbytes;
878
+
879
+ table->packing.indexset_size = ixsetbytes;
880
+ bytes += ixsetbytes;
881
+
882
+ //#########
883
+ // Row Data
884
+
885
+ // Call pack with PREPARE=true to compute byte count and mark
886
+ // all strings prior to packing.
887
+ table->packing.rowdata_bytes = pack_row_data<true>(table, NULL, NULL);
888
+ bytes += table->packing.rowdata_bytes;
889
+
890
+ table->packing.total_size = bytes;
891
+ return bytes;
892
+ }
893
+
894
+ static bool must_remap_fixedkeys(const table_ctx *table) {
895
+ return table->pending_deletes.size() &&
896
+ (table->fixed_dropped_sp == 0) &&
897
+ (table->pending_deletes.size() != (uint32) table->fixed.tabular.row_count);
898
+ }
899
+
900
+ //
901
+ // There are basically two types of columns. Direct and indirect values.
902
+ // For each row, we process the one type then the other.
903
+ //
904
+ struct consolidated_frow_strings
905
+ {
906
+ uint32 src_offset;
907
+ uint32 target_offset;
908
+ };
909
+ struct consolidated_frow_bytes : consolidated_frow_strings
910
+ {
911
+ uint32 width;
912
+ };
913
+ struct consolidated_frow_bits : consolidated_frow_strings
914
+ {
915
+ uint8 src_mask;
916
+ int8 rshift;
917
+ };
918
+
919
+ template<typename T>
920
+ struct prepare_pack_cb {
921
+ inline static bool call(const column_ctx &ctx, bool value, user_stringtable_t *strtable) { return true; }
922
+ inline static bool call(const column_ctx &ctx, datetime_t value, user_stringtable_t *strtable) { return true; }
923
+
924
+ inline static bool call(const column_ctx &ctx, int8 value, user_stringtable_t *strtable) { return true; }
925
+ inline static bool call(const column_ctx &ctx, int16 value, user_stringtable_t *strtable) { return true; }
926
+ inline static bool call(const column_ctx &ctx, int32 value, user_stringtable_t *strtable) { return true; }
927
+ inline static bool call(const column_ctx &ctx, int64 value, user_stringtable_t *strtable) { return true; }
928
+
929
+ inline static bool call(const column_ctx &ctx, uint8 value, user_stringtable_t *strtable) { return true; }
930
+ inline static bool call(const column_ctx &ctx, uint16 value, user_stringtable_t *strtable) { return true; }
931
+ inline static bool call(const column_ctx &ctx, uint32 value, user_stringtable_t *strtable) { return true; }
932
+ inline static bool call(const column_ctx &ctx, uint64 value, user_stringtable_t *strtable) { return true; }
933
+
934
+ inline static bool call(const column_ctx &ctx, float32 value, user_stringtable_t *strtable) { return true; }
935
+ inline static bool call(const column_ctx &ctx, float64 value, user_stringtable_t *strtable) { return true; }
936
+
937
+ // Only the variants and strings get called with safe_cv_t values.
938
+ inline static bool call(const column_ctx &ctx, const safe_cv_t &value, user_stringtable_t *strtable) {
939
+ OINKY_ASSERT((ctx.ctype == column_types::Variant) || (ctx.ctype == column_types::String));
940
+ if (value.is_string()) {
941
+ strtable->mark(value);
942
+ }
943
+ return true;
944
+ }
945
+ };
946
+
947
+ struct complete_pack_cb_ctx
948
+ {
949
+ char *rowbase;
950
+ user_stringtable_t *strtable;
951
+
952
+ complete_pack_cb_ctx(
953
+ char *_rowbase,
954
+ user_stringtable_t *_strtable) :
955
+ rowbase(_rowbase),
956
+ strtable(_strtable)
957
+ {}
958
+ };
959
+
960
+ template<typename T>
961
+ struct complete_pack_cb {
962
+ #define OINKY_DEFINE_COMPLETE_PACK_CB(TYPE) \
963
+ inline static bool call(const column_ctx &cc, TYPE value, const complete_pack_cb_ctx *ctx) { \
964
+ BOOST_STATIC_ASSERT((boost::is_same<TYPE, T>::value)); \
965
+ Serializer<TYPE, scheme>::pack_nbc(value, ctx->rowbase + cc.new_row_offset_bytes); \
966
+ return true; \
967
+ }
968
+ OINKY_DEFINE_COMPLETE_PACK_CB(int8)
969
+ OINKY_DEFINE_COMPLETE_PACK_CB(int16)
970
+ OINKY_DEFINE_COMPLETE_PACK_CB(int32)
971
+ OINKY_DEFINE_COMPLETE_PACK_CB(int64)
972
+
973
+ OINKY_DEFINE_COMPLETE_PACK_CB(uint8)
974
+ OINKY_DEFINE_COMPLETE_PACK_CB(uint16)
975
+ OINKY_DEFINE_COMPLETE_PACK_CB(uint32)
976
+ OINKY_DEFINE_COMPLETE_PACK_CB(uint64)
977
+
978
+ OINKY_DEFINE_COMPLETE_PACK_CB(float32)
979
+ OINKY_DEFINE_COMPLETE_PACK_CB(float64)
980
+
981
+ OINKY_DEFINE_COMPLETE_PACK_CB(datetime_t)
982
+ #undef OINKY_DEFINE_COMPLETE_PACK_CB
983
+
984
+ // Only the variants and strings get called with safe_cv_t values.
985
+ inline static bool call(const column_ctx &cc, bool value, const complete_pack_cb_ctx *ctx) {
986
+ OINKY_ASSERT(cc.ctype == column_types::Bit);
987
+ if (value) {
988
+ uint8 mask = 1 << cc.new_bit_offset;
989
+ ctx->rowbase[cc.new_row_offset_bytes] |= mask;
990
+ }
991
+ return true;
992
+ }
993
+ // Only the variants and strings get called with safe_cv_t values.
994
+ inline static bool call(const column_ctx &cc, const safe_cv_t &cv, const complete_pack_cb_ctx *ctx) {
995
+ OINKY_ASSERT((cc.ctype == column_types::Variant) || (cc.ctype == column_types::String));
996
+ char *cursor = ctx->rowbase + cc.new_row_offset_bytes;
997
+ if (cc.ctype == column_types::Variant) {
998
+ // First write the typecode.
999
+ *(value_type_code_t *)cursor = cv.type();
1000
+ cursor += 1;
1001
+ // Write the value.
1002
+ i_stored_value::pack_variant(cv, cursor, *ctx->strtable);
1003
+ } else {
1004
+ // Now pack the value...string or otherwise.
1005
+ i_stored_value::pack_string(cv, cursor, *ctx->strtable);
1006
+ }
1007
+ return true;
1008
+ }
1009
+ };
1010
+
1011
+ inline static void prepare_pack_prow(
1012
+ const column_ctx *const* cols_begin,
1013
+ const column_ctx *const* cols_end,
1014
+ user_stringtable_t *strtable,
1015
+ typename table_ctx::pitr_t &itr)
1016
+ {
1017
+ itr->template each_raw_value<prepare_pack_cb>(cols_begin, cols_end, strtable);
1018
+ }
1019
+
1020
+ inline static void complete_pack_prow(
1021
+ const table_ctx *table,
1022
+ typename table_ctx::pitr_t &itr,
1023
+ char *cursor)
1024
+ {
1025
+ // If we are storing any single-bit columns...
1026
+ if (table->packing.row_width.bit_count) {
1027
+ // Zero the bitfield, since we may not set them all.
1028
+ memset(cursor + table->packing.row_width.byte_offset, 0, (table->packing.row_width.bit_count + 7) >> 3);
1029
+ }
1030
+ complete_pack_cb_ctx ctx(cursor, &table->db->userstrings);
1031
+ itr->template each_raw_value<complete_pack_cb>(table->cols_by_position.begin(), table->cols_by_position.end(), &ctx);
1032
+ }
1033
+
1034
+ // This just marks the strings.
1035
+ inline static void prepare_pack_row_strings(
1036
+ table_ctx *table,
1037
+ user_stringtable_t &strtable,
1038
+ const consolidated_frow_strings *ci,
1039
+ const consolidated_frow_strings *coldefs_end,
1040
+ typename table_ctx::fixed_itr_t &itr)
1041
+ {
1042
+ row_idx_t rowkey = *itr;
1043
+ const char *src_base = table->fixed.tabular.rowdata_base +
1044
+ (table->fixed.tabular.row_width_bytes * rowkey);
1045
+
1046
+ for (;ci != coldefs_end; ++ci) {
1047
+ const char *svalue = src_base + ci->src_offset;
1048
+ u_strtable_ref sr;
1049
+ unpack_nbc<scheme>(&sr, svalue);
1050
+ strtable.mark_ref(sr);
1051
+ }
1052
+ }
1053
+ inline static void complete_pack_frow_strings(
1054
+ table_ctx *table,
1055
+ const user_stringtable_t &strtable,
1056
+ const consolidated_frow_strings *ci,
1057
+ const consolidated_frow_strings *coldefs_end,
1058
+ typename table_ctx::fixed_itr_t &itr,
1059
+ char *trowbase)
1060
+ {
1061
+ row_idx_t rowkey = *itr;
1062
+ const char *src_base = table->fixed.tabular.rowdata_base +
1063
+ (table->fixed.tabular.row_width_bytes * rowkey);
1064
+
1065
+ for (;ci != coldefs_end; ++ci) {
1066
+ const char *svalue = src_base + ci->src_offset;
1067
+ char *tvalue = trowbase + ci->target_offset;
1068
+ u_strtable_ref sr;
1069
+ unpack_nbc<scheme>(&sr, svalue);
1070
+ u_strtable_ref r = strtable.xform_ref(sr);
1071
+ OINKY_ASSERT(r);
1072
+ pack_nbc<scheme>(r, tvalue);
1073
+ }
1074
+ }
1075
+ inline static void prepare_pack_row_variants(
1076
+ table_ctx *table,
1077
+ user_stringtable_t &strtable,
1078
+ const consolidated_frow_strings *ci,
1079
+ const consolidated_frow_strings *coldefs_end,
1080
+ typename table_ctx::fixed_itr_t &itr)
1081
+ {
1082
+ row_idx_t rowkey = *itr;
1083
+ const char *src_base = table->fixed.tabular.rowdata_base +
1084
+ (table->fixed.tabular.row_width_bytes * rowkey);
1085
+
1086
+ for (;ci != coldefs_end; ++ci) {
1087
+ const char *svalue = src_base + ci->src_offset;
1088
+ if (*svalue == (char) value_types::String) {
1089
+ ++svalue;
1090
+ u_strtable_ref sr;
1091
+ unpack_nbc<scheme>(&sr, svalue);
1092
+ strtable.mark_ref(sr);
1093
+ }
1094
+ }
1095
+ }
1096
+ inline static void complete_pack_frow_variants(
1097
+ table_ctx *table,
1098
+ const user_stringtable_t &strtable,
1099
+ const consolidated_frow_strings *ci,
1100
+ const consolidated_frow_strings *coldefs_end,
1101
+ typename table_ctx::fixed_itr_t &itr,
1102
+ char *trowbase)
1103
+ {
1104
+ row_idx_t rowkey = *itr;
1105
+ const char *src_base = table->fixed.tabular.rowdata_base +
1106
+ (table->fixed.tabular.row_width_bytes * rowkey);
1107
+
1108
+ for (;ci != coldefs_end; ++ci) {
1109
+ const char *svalue = src_base + ci->src_offset;
1110
+ char *tvalue = trowbase + ci->target_offset;
1111
+ value_type_code_t vt = (value_type_code_t) *svalue;
1112
+ *tvalue = *svalue;
1113
+ ++svalue;
1114
+ ++tvalue;
1115
+ if (vt == value_types::String) {
1116
+ u_strtable_ref sr;
1117
+ unpack_nbc<scheme>(&sr, svalue);
1118
+ u_strtable_ref r = strtable.xform_ref(sr);
1119
+ OINKY_ASSERT(r);
1120
+ pack_nbc<scheme>(r, tvalue);
1121
+ // Two should fit in the 8 byte value cell.
1122
+ BOOST_STATIC_ASSERT(sizeof(u_strtable_ref) == 4);
1123
+ // Zero the remainder.
1124
+ tvalue += 4;
1125
+ *(uint32 *)tvalue = 0;
1126
+ } else {
1127
+ // Just copy the 8 bytes verbatim.
1128
+ *(uint64*)tvalue = *(const uint64*)svalue;
1129
+ }
1130
+ }
1131
+ }
1132
+ inline static void complete_pack_frow_bytes(
1133
+ table_ctx *table,
1134
+ const consolidated_frow_bytes *ci,
1135
+ const consolidated_frow_bytes *coldefs_end,
1136
+ typename table_ctx::fixed_itr_t &itr,
1137
+ char *trowbase)
1138
+ {
1139
+ row_idx_t rowkey = *itr;
1140
+ const char *src_base = table->fixed.tabular.rowdata_base +
1141
+ (table->fixed.tabular.row_width_bytes * rowkey);
1142
+
1143
+ for (;ci != coldefs_end; ++ci) {
1144
+ const char *svalue = src_base + ci->src_offset;
1145
+ char *tvalue = trowbase + ci->target_offset;
1146
+ memcpy(tvalue, svalue, ci->width);
1147
+ }
1148
+ }
1149
+
1150
+ inline static void complete_pack_frow_bits(
1151
+ table_ctx *table,
1152
+ const consolidated_frow_bits *ci,
1153
+ const consolidated_frow_bits *coldefs_end,
1154
+ typename table_ctx::fixed_itr_t &itr,
1155
+ char *trowbase)
1156
+ {
1157
+ row_idx_t rowkey = *itr;
1158
+ const char *src_base = table->fixed.tabular.rowdata_base +
1159
+ (table->fixed.tabular.row_width_bytes * rowkey);
1160
+
1161
+ for (;ci != coldefs_end; ++ci) {
1162
+ uint8 src = src_base[ci->src_offset] & ci->src_mask;
1163
+ // Not all architectures support negative shift, and it's undefined
1164
+ // in the language what it does, so I'm trusting the compiler to
1165
+ // optimize this if it can, but do the right thing generally.
1166
+ if (ci->rshift < 0) {
1167
+ int8 lshift = -ci->rshift;
1168
+ OINKY_ASSERT((src & ~((256>>lshift) - 1)) == 0);
1169
+ src <<= lshift;
1170
+ } else {
1171
+ // We should have already masked out everything we don't need.
1172
+ // We should not be losing any bits.
1173
+ OINKY_ASSERT((src & ((1<<ci->rshift) - 1)) == 0);
1174
+ src >>= ci->rshift;
1175
+ }
1176
+ uint8 *t = (uint8 *) trowbase + ci->target_offset;
1177
+ // The target row buffer was zeroed, so we don't have ot worry about
1178
+ // clearing aanything.
1179
+ *t |= src;
1180
+ }
1181
+ }
1182
+
1183
+ // This has two modes, one for each pass, PREPARE and FINAL.
1184
+ template<bool PREPARE>
1185
+ static uint32 pack_row_data(table_ctx *table, row_idx_t *remap_v, char *rowdatabegin)
1186
+ {
1187
+ char *rowcursor = rowdatabegin;
1188
+
1189
+ // Iterate through fixed entries.
1190
+ typename table_ctx::fixed_itr_t fi = table->fixed.begin();
1191
+ typename table_ctx::fixed_itr_t fe = table->fixed.end();
1192
+ uint32 new_rowkey = 0;
1193
+
1194
+ // How many fixed rows do we expect to write?
1195
+ uint32 fixed_rowcount =
1196
+ (table->fixed_dropped()) ?
1197
+ 0 :
1198
+ (table->fixed.tabular.row_count - table->pending_deletes.size());
1199
+
1200
+ // Only bother with this if we're going to write anything.
1201
+ if (fixed_rowcount) {
1202
+ OINKY_ASSERT(fi != fe);
1203
+ // Compute the column set summary (smaller and more local than
1204
+ // the full set of column contexts, plus with a simpler iterator).
1205
+ // In the fixed case we can eliminate branching on column type
1206
+ // in the inner loop.
1207
+ consolidated_frow_bytes *bdefs = (consolidated_frow_bytes *)
1208
+ alloca(sizeof(consolidated_frow_bytes) * table->cols_by_position.size());
1209
+ consolidated_frow_strings *sdefs = (consolidated_frow_strings *)
1210
+ alloca(sizeof(consolidated_frow_strings) * table->cols_by_position.size());
1211
+ consolidated_frow_strings *vdefs = (consolidated_frow_strings *)
1212
+ alloca(sizeof(consolidated_frow_strings) * table->cols_by_position.size());
1213
+ consolidated_frow_bits *btdefs = (consolidated_frow_bits *)
1214
+ alloca(sizeof(consolidated_frow_bits) * table->cols_by_position.size());
1215
+ consolidated_frow_bytes *bt = bdefs;
1216
+ consolidated_frow_bits *bitst = btdefs;
1217
+ consolidated_frow_strings *st = sdefs;
1218
+ consolidated_frow_strings *vt = vdefs;
1219
+ uint32 this_bit = 0;
1220
+ cols_itr_t ci = table->cols_by_position.begin();
1221
+ cols_itr_t cend = table->cols_by_position.end();
1222
+ for (; ci != cend; ++ci)
1223
+ {
1224
+ const column_ctx &cc(**ci);
1225
+ // Fixed rows imply no schema change.
1226
+ OINKY_ASSERT((cc.ls.insert_sp.val | cc.ls.delete_sp.val) == 0);
1227
+
1228
+ if (cc.ctype == column_types::Variant) {
1229
+ vt->src_offset = cc.frow_byte_offset;
1230
+ vt->target_offset = cc.new_row_offset_bytes;
1231
+ ++vt;
1232
+ } else if (cc.ctype == column_types::String) {
1233
+ st->src_offset = cc.frow_byte_offset;
1234
+ st->target_offset = cc.new_row_offset_bytes;
1235
+ ++st;
1236
+ } else if (!PREPARE) {
1237
+ //
1238
+ // These only need be computed for complete_pack
1239
+ //
1240
+ if (cc.ctype == column_types::Bit) {
1241
+ bitst->src_offset = cc.frow_byte_offset;
1242
+ bitst->target_offset = table->packing.row_width.byte_offset + (this_bit >> 3);
1243
+ OINKY_ASSERT(cc.frow_bit_offset < 8);
1244
+ bitst->src_mask = 1 << cc.frow_bit_offset;
1245
+ bitst->rshift = cc.frow_bit_offset - (this_bit & 7);
1246
+ ++this_bit;
1247
+
1248
+ // Merge into previous if we can. This is the common case.
1249
+ if ((bitst != btdefs) &&
1250
+ ((bitst-1)->src_offset == bitst->src_offset) &&
1251
+ ((bitst-1)->target_offset == bitst->target_offset) &&
1252
+ ((bitst-1)->rshift == bitst->rshift))
1253
+ {
1254
+ OINKY_ASSERT(((bitst-1)->src_mask & bitst->src_mask) == 0);
1255
+ (bitst-1)->src_mask |= bitst->src_mask;
1256
+ } else {
1257
+ ++bitst;
1258
+ }
1259
+ } else {
1260
+ bt->src_offset = cc.frow_byte_offset;
1261
+ bt->width = compute_serialized_width(cc.ctype);
1262
+ bt->target_offset = cc.new_row_offset_bytes;
1263
+ // Extend the limit or append to the previous.
1264
+ if ((bt != bdefs) &&
1265
+ ((bt-1)->src_offset + (bt-1)->width == bt->src_offset) &&
1266
+ ((bt-1)->target_offset + (bt-1)->width == bt->target_offset))
1267
+ {
1268
+ (bt-1)->width += bt->width;
1269
+ } else {
1270
+ ++bt;
1271
+ }
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ if (PREPARE) {
1277
+ // Mark the strings
1278
+ for (;fi != fe; ++fi) {
1279
+ if (fi.is_deleted()) {
1280
+ continue;
1281
+ }
1282
+ prepare_pack_row_strings(table, table->db->userstrings, sdefs, st, fi);
1283
+ prepare_pack_row_variants(table, table->db->userstrings, vdefs, vt, fi);
1284
+ ++new_rowkey;
1285
+ }
1286
+ // Just advance rowcursor all at once.
1287
+ rowcursor += table->packing.row_width.full_width() * new_rowkey;
1288
+ } else {
1289
+ uint32 bitfield_offset = table->packing.row_width.byte_offset;
1290
+ uint32 full_row_width = table->packing.row_width.full_width();
1291
+ uint32 bitfield_width = table->packing.row_width.full_width() - bitfield_offset;
1292
+ // Iterate over fixed rows.
1293
+ for (;fi != fe; ++fi) {
1294
+ if (fi.is_deleted()) {
1295
+ continue;
1296
+ }
1297
+
1298
+ // Pack the string values. Neither of the following routines
1299
+ // updates rowcursor. We do that at the end of this iteration.
1300
+ complete_pack_frow_strings(table, table->db->userstrings, sdefs, st, fi, rowcursor);
1301
+ complete_pack_frow_variants(table, table->db->userstrings, vdefs, vt, fi, rowcursor);
1302
+ // Pack the string values.
1303
+ complete_pack_frow_bytes(table, bdefs, bt, fi, rowcursor);
1304
+ if (bitst != btdefs) {
1305
+ // Zero the target bitfield, so we can focus on setting only.
1306
+ memset(rowcursor + bitfield_offset, 0, bitfield_width);
1307
+ complete_pack_frow_bits(table, btdefs, bitst, fi, rowcursor);
1308
+ }
1309
+ // Update the row re-map.
1310
+ row_idx_t oldrowkey = *fi;
1311
+ if (remap_v) {
1312
+ remap_v[oldrowkey] = new_rowkey;
1313
+ }
1314
+ // Advance row cursor and rowkey.
1315
+ rowcursor += full_row_width;
1316
+ ++new_rowkey;
1317
+ }
1318
+ }
1319
+ } //if (fixed_rowcount)
1320
+
1321
+ OINKY_ASSERT(fixed_rowcount == new_rowkey);
1322
+
1323
+ // Now the dynamic entries.
1324
+ typename table_ctx::pitr_t di = table->live_pending.begin();
1325
+ typename table_ctx::pitr_t de = table->live_pending.end();
1326
+
1327
+ if (table->live_pending.size()) {
1328
+ // Now write the rows.
1329
+ uint32 serialized_row_width = table->packing.row_width.full_width();
1330
+ if (PREPARE) {
1331
+ // During prepare, we only need to iterate over the indirect
1332
+ // columns. Create a consolidated set of columns for this
1333
+ // pass.
1334
+ const column_ctx **defs = (const column_ctx **) alloca(sizeof(const column_ctx *) * table->cols_by_position.size());
1335
+ const column_ctx **t = defs;
1336
+
1337
+ cols_itr_t ci = table->cols_by_position.begin();
1338
+ cols_itr_t cend = table->cols_by_position.end();
1339
+ for (;ci != cend; ++ci) {
1340
+ if (((*ci)->ctype == column_types::Variant) || ((*ci)->ctype == column_types::String)) {
1341
+ *t = *ci;
1342
+ ++t;
1343
+ }
1344
+ }
1345
+
1346
+ // Iterate rows and mark strings.
1347
+ for (;di != de; ++di) {
1348
+ prepare_pack_prow(defs, t, &table->db->userstrings, di);
1349
+ ++new_rowkey;
1350
+ }
1351
+ rowcursor += serialized_row_width * table->live_pending.size();
1352
+ } else {
1353
+ for (;di != de; ++di) {
1354
+ complete_pack_prow(table, di, rowcursor);
1355
+ rowcursor += serialized_row_width;
1356
+ di->new_rowkey = new_rowkey;
1357
+ ++new_rowkey;
1358
+ }
1359
+ }
1360
+ }
1361
+ OINKY_ASSERT(new_rowkey == table->row_count());
1362
+ return rowcursor - rowdatabegin;
1363
+ }
1364
+
1365
+ static void complete_pack_table(
1366
+ table_ctx *table,
1367
+ char *&header_cursor,
1368
+ char *tableset_base,
1369
+ const char *end_buf,
1370
+ uint32 *last_data_offset)
1371
+ {
1372
+ //###############
1373
+ // First prepare the header.
1374
+ s_table_hdr::pack(
1375
+ // Extract the new stringreference, which may differ from the old,
1376
+ // if it existed at all before.
1377
+ table->db->metastrings.xform_ref(table->tablename),
1378
+ // This routine will alternate with complete_pack_table during
1379
+ // serialization, and that routine will advance this pointer after
1380
+ // packing this table.
1381
+ *last_data_offset,
1382
+ header_cursor,
1383
+ end_buf);
1384
+
1385
+ //###############
1386
+ // Now the table context itself.
1387
+ char *write_cursor = tableset_base + *last_data_offset;
1388
+
1389
+ Serializer<row_idx_t, scheme>::pack((row_idx_t) table->row_count(), write_cursor, end_buf);
1390
+ // Save this position so we can write the rowdata offset once we know it.
1391
+ //
1392
+ uint32 rowdataoffset =
1393
+ write_cursor - tableset_base + //here
1394
+ 4 + // + self
1395
+ table->columns.computed_packed_header_bytes + // column data
1396
+ table->packing.indexset_size; // index_data;
1397
+ Serializer<uint32, scheme>::pack(rowdataoffset, write_cursor, end_buf);//rowdata_offset
1398
+
1399
+ // Pack the column data
1400
+ complete_pack_set(
1401
+ table->columns,
1402
+ write_cursor,
1403
+ boost::bind(&complete_pack_column, _1, _2, table->db, end_buf));
1404
+
1405
+ OINKY_ASSERT( write_cursor - tableset_base +
1406
+ table->packing.indexset_size == rowdataoffset);
1407
+
1408
+ // The index data appears next in the buffer, but before we can store
1409
+ // it we need to calculate the new row positions.
1410
+ char *ixsetbegin = write_cursor;
1411
+
1412
+ //###############
1413
+ // Rowdata
1414
+ row_idx_t *remap_v = NULL;
1415
+ if (must_remap_fixedkeys(table))
1416
+ {
1417
+ // In this case the fixed set was partially but not completely deleted.
1418
+ // We need to remap it.
1419
+ OINKY_ASSERT((uint32) table->fixed.tabular.row_count <= table->db->packing.max_fixed_table_remap_size);
1420
+
1421
+ // check-alloc remap buffer.
1422
+ // This will be zero if we've never initialized it. When we initialize it,
1423
+ // we set it to the max table size discovered during pack. However, it
1424
+ // may still be smaller than the present table if subsequent to
1425
+ // a previous pack, a savepoint was reverted, restoring a deleted table
1426
+ // larger than all the others.
1427
+ if (table->db->packing.row_remap_vector.size() < (uint32) table->fixed.tabular.row_count) {
1428
+ // Assign with zero size (so we don't create a temporary), in order
1429
+ // to assign the allocator.
1430
+ table->db->packing.row_remap_vector = db_vector<row_idx_t>(&table->db->allocator);
1431
+ // Now resize in place.
1432
+ // Resize to zero first so that we don't bother copying the old (junk) data.
1433
+ table->db->packing.row_remap_vector.resize(0);
1434
+ table->db->packing.row_remap_vector.resize(table->db->packing.max_fixed_table_remap_size);
1435
+ }
1436
+ remap_v = table->db->packing.row_remap_vector.begin();
1437
+ }
1438
+
1439
+ // Pack the row data.
1440
+ char *rowdatabegin = ixsetbegin + table->packing.indexset_size;
1441
+ OINKY_ASSERT( tableset_base + rowdataoffset == rowdatabegin );
1442
+ uint32 rowdata_bytes = pack_row_data<false>(table, remap_v, rowdatabegin);
1443
+ OINKY_ASSERT(rowdata_bytes == table->packing.rowdata_bytes);
1444
+
1445
+ uint32 next_tbl_data_offset = rowdatabegin + rowdata_bytes - tableset_base;
1446
+ OINKY_ASSERT(next_tbl_data_offset - *last_data_offset == table->packing.total_size);
1447
+ *last_data_offset = next_tbl_data_offset;
1448
+
1449
+ // Write the rowdata pointer.
1450
+
1451
+ //#########
1452
+ // Index data.
1453
+ write_cursor = ixsetbegin;
1454
+ const char *ixend = ixsetbegin + table->packing.indexset_size;
1455
+ Serializer<uint32, scheme>::pack(table->packing.indexset_size, write_cursor, ixend);
1456
+
1457
+ char *prehdr = write_cursor;
1458
+ uint32 indexdata_offset = (write_cursor - ixsetbegin) +
1459
+ table->indexes.computed_packed_header_bytes;
1460
+
1461
+ complete_pack_set(
1462
+ table->indexes,
1463
+ write_cursor,
1464
+ boost::bind(
1465
+ &complete_pack_index,
1466
+ _1,
1467
+ _2,
1468
+ ixsetbegin,
1469
+ end_buf,
1470
+ &indexdata_offset,
1471
+ remap_v));
1472
+
1473
+ OINKY_ASSERT(write_cursor == prehdr + table->indexes.computed_packed_header_bytes);
1474
+ OINKY_ASSERT(indexdata_offset == table->packing.indexset_size);
1475
+ }
1476
+
1477
+ template<typename HDR_T, typename CTX_TRAITS, typename FN>
1478
+ static uint32 prepare_pack_set(context_map_t<CTX_TRAITS> &map, FN fn)
1479
+ {
1480
+ typedef typename CTX_TRAITS::index_t index_t;
1481
+ typedef context_map_t<CTX_TRAITS> map_t;
1482
+ typedef typename map_t::by_name_iterator bni;
1483
+
1484
+ bni i = map.by_name().begin();
1485
+ bni end = map.by_name().end();
1486
+
1487
+ uint32 bytes = 0;
1488
+ uint32 j = 0;
1489
+ for (;i != end; ++i, ++j) {
1490
+ // Reference the metastring, so it will get stored.
1491
+ map.stringtable->mark(CTX_TRAITS::key_from_ctx(*i));
1492
+
1493
+ bytes += fn(*i);
1494
+ }
1495
+ map.computed_packed_header_bytes = j * sizeof(HDR_T);
1496
+ // For the count.
1497
+ map.computed_packed_header_bytes += Serializer<index_t,scheme>::virtual_pack();
1498
+
1499
+ bytes += map.computed_packed_header_bytes;
1500
+ return bytes;
1501
+ }
1502
+
1503
+ template<typename CTX_TRAITS, typename FN>
1504
+ static void complete_pack_set(
1505
+ context_map_t<CTX_TRAITS> &map,
1506
+ char *&cursor,
1507
+ FN fn)
1508
+ {
1509
+ typedef typename CTX_TRAITS::index_t index_t;
1510
+ typedef context_map_t<CTX_TRAITS> map_t;
1511
+ typedef typename map_t::by_name_iterator bni;
1512
+
1513
+ bni end = map.by_name().end();
1514
+
1515
+ // Save the position where we'll write the count.
1516
+ char *count_cursor = cursor;
1517
+ cursor += Serializer<index_t,scheme>::virtual_pack();
1518
+
1519
+ uint32 j = 0;
1520
+ // Write each of the headers
1521
+ for (bni i = map.by_name().begin();i != end; ++i, ++j) {
1522
+ fn(*i, cursor);
1523
+ }
1524
+
1525
+ // Write the count.
1526
+ Serializer<index_t,scheme>::pack((index_t) j, count_cursor, cursor);
1527
+ }
1528
+
1529
+ // This does two things. One is it does an initial pack phase, preparing
1530
+ // the database for serialization. This may allocate memory internally
1531
+ // for sorting or other operations which are required prior to pack.
1532
+ // It returns the number of bytes that will be required of the buffer
1533
+ // to contain the serialized database. complete_pack should be called
1534
+ // after phase1_pack, with no intervening changes.
1535
+ static uint32 prepare_pack(db_handle_internal &db)
1536
+ {
1537
+ uint32 bytes = 0;
1538
+ // Encoding scheme.
1539
+ bytes += Serializer<scheme,scheme>::virtual_pack();
1540
+
1541
+ // DB-size : We always serialize as a 4-byte integer.
1542
+ bytes += Serializer<uint32,scheme>::virtual_pack();
1543
+
1544
+ // Clear stringtable reference counts. Allocate vectors to
1545
+ // store reference counts if necessary.
1546
+ db.metastrings.clear_references();
1547
+ db.userstrings.clear_references();
1548
+
1549
+ uint32 tblbytes = 0;
1550
+ // indicator - size in bytes of the serialied tableset.
1551
+ tblbytes += Serializer<uint32,scheme>::virtual_pack();
1552
+
1553
+ // Compute the space required to store all tables.
1554
+ // While we're at it, mark all referenced strings.
1555
+ tblbytes += prepare_pack_set<s_table_hdr>(
1556
+ db.tables,
1557
+ boost::bind(&prepare_pack_table, _1, &db));
1558
+
1559
+ // Use string markings to transform string indexes, and compute
1560
+ // new stringtable sizes.
1561
+ //
1562
+ // These are NOT part of the tableset.
1563
+ bytes += db.metastrings.compute_indexes_and_length();
1564
+ bytes += db.userstrings.compute_indexes_and_length();
1565
+
1566
+ // Remember the computed size.
1567
+ db.packing.tableset_size = tblbytes;
1568
+ db.packing.total_bytes = bytes + tblbytes;
1569
+
1570
+ // The trailer.
1571
+ db.packing.total_bytes += Serializer<v1_trailer, v1_sformat_tag>::virtual_pack();
1572
+
1573
+ return db.packing.total_bytes;
1574
+ }
1575
+
1576
+ // This must be called after prepare_pack. If any changes to the database
1577
+ // are made in between, the resulting serialized database is likely to be
1578
+ // corrupted.
1579
+ static void complete_pack(db_handle_internal &db, char *&next_buffer, const char *end_buf)
1580
+ {
1581
+ char *begin = next_buffer;
1582
+ if (end_buf - begin < db.packing.total_bytes) {
1583
+ throw_error(buffer_overflow());
1584
+ }
1585
+ // Limit end buffer to what we computed.
1586
+ end_buf = begin + db.packing.total_bytes;
1587
+
1588
+ // Check the encoding.
1589
+ // This will throw if the scheme is wrong.
1590
+ Serializer<scheme,scheme>::pack(next_buffer, end_buf);
1591
+
1592
+ Serialization::pack<scheme>(db.packing.total_bytes, next_buffer, end_buf);
1593
+
1594
+ // Pack the string tables. The string tables aren't exactly separable
1595
+ // from their encoding, so we just call member functions.
1596
+ db.metastrings.pack<scheme>(next_buffer, end_buf);
1597
+ db.userstrings.pack<scheme>(next_buffer, end_buf);
1598
+
1599
+ // Write the tableset map and tables.
1600
+ char *const tableset_begin(next_buffer);
1601
+ Serialization::pack<scheme>(db.packing.tableset_size, next_buffer, end_buf);
1602
+
1603
+ uint32 last_tableset_data_offset =
1604
+ (next_buffer - tableset_begin) +
1605
+ db.tables.computed_packed_header_bytes;
1606
+
1607
+ complete_pack_set(
1608
+ db.tables,
1609
+ next_buffer,
1610
+ boost::bind(&complete_pack_table, _1, _2, tableset_begin, end_buf, &last_tableset_data_offset));
1611
+
1612
+ // The tableset is framed by the DB size itself.
1613
+ OINKY_ASSERT(last_tableset_data_offset == db.packing.tableset_size);
1614
+ next_buffer = tableset_begin + last_tableset_data_offset;
1615
+ OINKY_ASSERT(next_buffer + 4 == end_buf);
1616
+
1617
+ // Write the trailer.
1618
+ Serializer<v1_trailer, v1_sformat_tag>::pack(next_buffer, end_buf);
1619
+ OINKY_ASSERT(next_buffer == end_buf);
1620
+ }
1621
+ };
1622
+
1623
+ } //namespace Serialization
1624
+ } //namespace Oinky
1625
+