oinky 0.1.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 (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
+