1 /*-
2 * Copyright (c) 2014-2018 MongoDB, Inc.
3 * Copyright (c) 2008-2014 WiredTiger, Inc.
4 * All rights reserved.
5 *
6 * See the file LICENSE for redistribution information.
7 */
8
9 /*
10 * Throughout this code we have to be aware of default argument conversion.
11 *
12 * Refer to Chapter 8 of "Expert C Programming" by Peter van der Linden for the
13 * gory details. The short version is that we have less cases to deal with
14 * because the compiler promotes shorter types to int or unsigned int.
15 */
16 typedef struct {
17 union {
18 int64_t i;
19 uint64_t u;
20 const char *s;
21 WT_ITEM item;
22 } u;
23 uint32_t size;
24 int8_t havesize;
25 char type;
26 } WT_PACK_VALUE;
27
28 /* Default to size = 1 if there is no size prefix. */
29 #define WT_PACK_VALUE_INIT { { 0 }, 1, 0, 0 }
30 #define WT_DECL_PACK_VALUE(pv) WT_PACK_VALUE pv = WT_PACK_VALUE_INIT
31
32 typedef struct {
33 WT_SESSION_IMPL *session;
34 const char *cur, *end, *orig;
35 unsigned long repeats;
36 WT_PACK_VALUE lastv;
37 } WT_PACK;
38
39 #define WT_PACK_INIT { NULL, NULL, NULL, NULL, 0, WT_PACK_VALUE_INIT }
40 #define WT_DECL_PACK(pack) WT_PACK pack = WT_PACK_INIT
41
42 typedef struct {
43 WT_CONFIG config;
44 char buf[20];
45 int count;
46 bool iskey;
47 int genname;
48 } WT_PACK_NAME;
49
50 /*
51 * __pack_initn --
52 * Initialize a pack iterator with the specified string and length.
53 */
54 static inline int
__pack_initn(WT_SESSION_IMPL * session,WT_PACK * pack,const char * fmt,size_t len)55 __pack_initn(
56 WT_SESSION_IMPL *session, WT_PACK *pack, const char *fmt, size_t len)
57 {
58 if (*fmt == '@' || *fmt == '<' || *fmt == '>')
59 return (EINVAL);
60 if (*fmt == '.')
61 ++fmt;
62
63 pack->session = session;
64 pack->cur = pack->orig = fmt;
65 pack->end = fmt + len;
66 pack->repeats = 0;
67 return (0);
68 }
69
70 /*
71 * __pack_init --
72 * Initialize a pack iterator with the specified string.
73 */
74 static inline int
__pack_init(WT_SESSION_IMPL * session,WT_PACK * pack,const char * fmt)75 __pack_init(WT_SESSION_IMPL *session, WT_PACK *pack, const char *fmt)
76 {
77 return (__pack_initn(session, pack, fmt, strlen(fmt)));
78 }
79
80 /*
81 * __pack_name_init --
82 * Initialize the name of a pack iterator.
83 */
84 static inline void
__pack_name_init(WT_SESSION_IMPL * session,WT_CONFIG_ITEM * names,bool iskey,WT_PACK_NAME * pn)85 __pack_name_init(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *names,
86 bool iskey, WT_PACK_NAME *pn)
87 {
88 WT_CLEAR(*pn);
89 pn->iskey = iskey;
90
91 if (names->str != NULL)
92 __wt_config_subinit(session, &pn->config, names);
93 else
94 pn->genname = 1;
95 }
96
97 /*
98 * __pack_name_next --
99 * Get the next field type from a pack iterator.
100 */
101 static inline int
__pack_name_next(WT_PACK_NAME * pn,WT_CONFIG_ITEM * name)102 __pack_name_next(WT_PACK_NAME *pn, WT_CONFIG_ITEM *name)
103 {
104 WT_CONFIG_ITEM ignore;
105
106 if (pn->genname) {
107 WT_RET(__wt_snprintf(pn->buf, sizeof(pn->buf),
108 (pn->iskey ? "key%d" : "value%d"), pn->count));
109 WT_CLEAR(*name);
110 name->str = pn->buf;
111 name->len = strlen(pn->buf);
112 name->type = WT_CONFIG_ITEM_STRING;
113 pn->count++;
114 }
115 else
116 WT_RET(__wt_config_next(&pn->config, name, &ignore));
117
118 return (0);
119 }
120
121 /*
122 * __pack_next --
123 * Next pack iterator.
124 */
125 static inline int
__pack_next(WT_PACK * pack,WT_PACK_VALUE * pv)126 __pack_next(WT_PACK *pack, WT_PACK_VALUE *pv)
127 {
128 char *endsize;
129
130 if (pack->repeats > 0) {
131 *pv = pack->lastv;
132 --pack->repeats;
133 return (0);
134 }
135
136 next: if (pack->cur == pack->end)
137 return (WT_NOTFOUND);
138
139 if (__wt_isdigit((u_char)*pack->cur)) {
140 pv->havesize = 1;
141 pv->size = WT_STORE_SIZE(strtoul(pack->cur, &endsize, 10));
142 pack->cur = endsize;
143 } else {
144 pv->havesize = 0;
145 pv->size = 1;
146 }
147
148 pv->type = *pack->cur++;
149 pack->repeats = 0;
150
151 switch (pv->type) {
152 case 'S':
153 return (0);
154 case 's':
155 if (pv->size < 1)
156 WT_RET_MSG(pack->session, EINVAL,
157 "Fixed length strings must be at least 1 byte "
158 "in format '%.*s'",
159 (int)(pack->end - pack->orig), pack->orig);
160 return (0);
161 case 'x':
162 return (0);
163 case 't':
164 if (pv->size < 1 || pv->size > 8)
165 WT_RET_MSG(pack->session, EINVAL,
166 "Bitfield sizes must be between 1 and 8 bits "
167 "in format '%.*s'",
168 (int)(pack->end - pack->orig), pack->orig);
169 return (0);
170 case 'u':
171 /* Special case for items with a size prefix. */
172 pv->type = (!pv->havesize && *pack->cur != '\0') ? 'U' : 'u';
173 return (0);
174 case 'U':
175 /*
176 * Don't change the type. 'U' is used internally, so this type
177 * was already changed to explicitly include the size.
178 */
179 return (0);
180 case 'b':
181 case 'h':
182 case 'i':
183 case 'B':
184 case 'H':
185 case 'I':
186 case 'l':
187 case 'L':
188 case 'q':
189 case 'Q':
190 case 'r':
191 case 'R':
192 /* Integral types repeat <size> times. */
193 if (pv->size == 0)
194 goto next;
195 pv->havesize = 0;
196 pack->repeats = pv->size - 1;
197 pack->lastv = *pv;
198 return (0);
199 default:
200 WT_RET_MSG(pack->session, EINVAL,
201 "Invalid type '%c' found in format '%.*s'",
202 pv->type, (int)(pack->end - pack->orig), pack->orig);
203 }
204
205 }
206
207 #define WT_PACK_GET(session, pv, ap) do { \
208 WT_ITEM *__item; \
209 switch ((pv).type) { \
210 case 'x': \
211 break; \
212 case 's': \
213 case 'S': \
214 (pv).u.s = va_arg(ap, const char *); \
215 break; \
216 case 'U': \
217 case 'u': \
218 __item = va_arg(ap, WT_ITEM *); \
219 (pv).u.item.data = __item->data; \
220 (pv).u.item.size = __item->size; \
221 break; \
222 case 'b': \
223 case 'h': \
224 case 'i': \
225 (pv).u.i = va_arg(ap, int); \
226 break; \
227 case 'B': \
228 case 'H': \
229 case 'I': \
230 case 't': \
231 (pv).u.u = va_arg(ap, unsigned int); \
232 break; \
233 case 'l': \
234 (pv).u.i = va_arg(ap, long); \
235 break; \
236 case 'L': \
237 (pv).u.u = va_arg(ap, unsigned long); \
238 break; \
239 case 'q': \
240 (pv).u.i = va_arg(ap, int64_t); \
241 break; \
242 case 'Q': \
243 case 'r': \
244 case 'R': \
245 (pv).u.u = va_arg(ap, uint64_t); \
246 break; \
247 /* User format strings have already been validated. */ \
248 WT_ILLEGAL_VALUE(session, (pv).type); \
249 } \
250 } while (0)
251
252 /*
253 * __pack_size --
254 * Get the size of a packed value.
255 */
256 static inline int
__pack_size(WT_SESSION_IMPL * session,WT_PACK_VALUE * pv,size_t * vp)257 __pack_size(WT_SESSION_IMPL *session, WT_PACK_VALUE *pv, size_t *vp)
258 {
259 size_t s, pad;
260
261 switch (pv->type) {
262 case 'x':
263 *vp = pv->size;
264 return (0);
265 case 'j':
266 case 'J':
267 case 'K':
268 /* These formats are only used internally. */
269 if (pv->type == 'j' || pv->havesize)
270 s = pv->size;
271 else {
272 ssize_t len;
273
274 /* The string was previously validated. */
275 len = __wt_json_strlen(pv->u.item.data,
276 pv->u.item.size);
277 WT_ASSERT(session, len >= 0);
278 s = (size_t)len + (pv->type == 'K' ? 0 : 1);
279 }
280 *vp = s;
281 return (0);
282 case 's':
283 case 'S':
284 if (pv->type == 's' || pv->havesize) {
285 s = pv->size;
286 WT_ASSERT(session, s != 0);
287 } else
288 s = strlen(pv->u.s) + 1;
289 *vp = s;
290 return (0);
291 case 'U':
292 case 'u':
293 s = pv->u.item.size;
294 pad = 0;
295 if (pv->havesize && pv->size < s)
296 s = pv->size;
297 else if (pv->havesize)
298 pad = pv->size - s;
299 if (pv->type == 'U')
300 s += __wt_vsize_uint(s + pad);
301 *vp = s + pad;
302 return (0);
303 case 'b':
304 case 'B':
305 case 't':
306 *vp = 1;
307 return (0);
308 case 'h':
309 case 'i':
310 case 'l':
311 case 'q':
312 *vp = __wt_vsize_int(pv->u.i);
313 return (0);
314 case 'H':
315 case 'I':
316 case 'L':
317 case 'Q':
318 case 'r':
319 *vp = __wt_vsize_uint(pv->u.u);
320 return (0);
321 case 'R':
322 *vp = sizeof(uint64_t);
323 return (0);
324 }
325
326 WT_RET_MSG(
327 session, EINVAL, "unknown pack-value type: %c", (int)pv->type);
328 }
329
330 /*
331 * __pack_write --
332 * Pack a value into a buffer.
333 */
334 static inline int
__pack_write(WT_SESSION_IMPL * session,WT_PACK_VALUE * pv,uint8_t ** pp,size_t maxlen)335 __pack_write(
336 WT_SESSION_IMPL *session, WT_PACK_VALUE *pv, uint8_t **pp, size_t maxlen)
337 {
338 size_t s, pad;
339 uint8_t *oldp;
340
341 switch (pv->type) {
342 case 'x':
343 WT_SIZE_CHECK_PACK(pv->size, maxlen);
344 memset(*pp, 0, pv->size);
345 *pp += pv->size;
346 break;
347 case 's':
348 WT_SIZE_CHECK_PACK(pv->size, maxlen);
349 memcpy(*pp, pv->u.s, pv->size);
350 *pp += pv->size;
351 break;
352 case 'S':
353 /*
354 * When preceded by a size, that indicates the maximum number
355 * of bytes the string can store, this does not include the
356 * terminating NUL character. In a string with characters
357 * less than the specified size, the remaining bytes are
358 * NULL padded.
359 */
360 if (pv->havesize) {
361 s = __wt_strnlen(pv->u.s, pv->size);
362 pad = (s < pv->size) ? pv->size - s : 0;
363 } else {
364 s = strlen(pv->u.s);
365 pad = 1;
366 }
367 WT_SIZE_CHECK_PACK(s + pad, maxlen);
368 if (s > 0)
369 memcpy(*pp, pv->u.s, s);
370 *pp += s;
371 if (pad > 0) {
372 memset(*pp, 0, pad);
373 *pp += pad;
374 }
375 break;
376 case 'j':
377 case 'J':
378 case 'K':
379 /* These formats are only used internally. */
380 s = pv->u.item.size;
381 if ((pv->type == 'j' || pv->havesize) && pv->size < s) {
382 s = pv->size;
383 pad = 0;
384 } else if (pv->havesize)
385 pad = pv->size - s;
386 else if (pv->type == 'K')
387 pad = 0;
388 else
389 pad = 1;
390 if (s > 0) {
391 oldp = *pp;
392 WT_RET(__wt_json_strncpy((WT_SESSION *)session,
393 (char **)pp, maxlen, pv->u.item.data, s));
394 maxlen -= (size_t)(*pp - oldp);
395 }
396 if (pad > 0) {
397 WT_SIZE_CHECK_PACK(pad, maxlen);
398 memset(*pp, 0, pad);
399 *pp += pad;
400 }
401 break;
402 case 'U':
403 case 'u':
404 s = pv->u.item.size;
405 pad = 0;
406 if (pv->havesize && pv->size < s)
407 s = pv->size;
408 else if (pv->havesize)
409 pad = pv->size - s;
410 if (pv->type == 'U') {
411 oldp = *pp;
412 /*
413 * Check that there is at least one byte available: the
414 * low-level routines treat zero length as unchecked.
415 */
416 WT_SIZE_CHECK_PACK(1, maxlen);
417 WT_RET(__wt_vpack_uint(pp, maxlen, s + pad));
418 maxlen -= (size_t)(*pp - oldp);
419 }
420 WT_SIZE_CHECK_PACK(s + pad, maxlen);
421 if (s > 0)
422 memcpy(*pp, pv->u.item.data, s);
423 *pp += s;
424 if (pad > 0) {
425 memset(*pp, 0, pad);
426 *pp += pad;
427 }
428 break;
429 case 'b':
430 /* Translate to maintain ordering with the sign bit. */
431 WT_SIZE_CHECK_PACK(1, maxlen);
432 **pp = (uint8_t)(pv->u.i + 0x80);
433 *pp += 1;
434 break;
435 case 'B':
436 case 't':
437 WT_SIZE_CHECK_PACK(1, maxlen);
438 **pp = (uint8_t)pv->u.u;
439 *pp += 1;
440 break;
441 case 'h':
442 case 'i':
443 case 'l':
444 case 'q':
445 /*
446 * Check that there is at least one byte available: the
447 * low-level routines treat zero length as unchecked.
448 */
449 WT_SIZE_CHECK_PACK(1, maxlen);
450 WT_RET(__wt_vpack_int(pp, maxlen, pv->u.i));
451 break;
452 case 'H':
453 case 'I':
454 case 'L':
455 case 'Q':
456 case 'r':
457 /*
458 * Check that there is at least one byte available: the
459 * low-level routines treat zero length as unchecked.
460 */
461 WT_SIZE_CHECK_PACK(1, maxlen);
462 WT_RET(__wt_vpack_uint(pp, maxlen, pv->u.u));
463 break;
464 case 'R':
465 WT_SIZE_CHECK_PACK(sizeof(uint64_t), maxlen);
466 *(uint64_t *)*pp = pv->u.u;
467 *pp += sizeof(uint64_t);
468 break;
469 default:
470 WT_RET_MSG(session, EINVAL,
471 "unknown pack-value type: %c", (int)pv->type);
472 }
473
474 return (0);
475 }
476
477 /*
478 * __unpack_read --
479 * Read a packed value from a buffer.
480 */
481 static inline int
__unpack_read(WT_SESSION_IMPL * session,WT_PACK_VALUE * pv,const uint8_t ** pp,size_t maxlen)482 __unpack_read(WT_SESSION_IMPL *session,
483 WT_PACK_VALUE *pv, const uint8_t **pp, size_t maxlen)
484 {
485 size_t s;
486
487 switch (pv->type) {
488 case 'x':
489 WT_SIZE_CHECK_UNPACK(pv->size, maxlen);
490 *pp += pv->size;
491 break;
492 case 's':
493 case 'S':
494 if (pv->type == 's' || pv->havesize) {
495 s = pv->size;
496 WT_ASSERT(session, s != 0);
497 } else
498 s = strlen((const char *)*pp) + 1;
499 if (s > 0)
500 pv->u.s = (const char *)*pp;
501 WT_SIZE_CHECK_UNPACK(s, maxlen);
502 *pp += s;
503 break;
504 case 'U':
505 /*
506 * Check that there is at least one byte available: the
507 * low-level routines treat zero length as unchecked.
508 */
509 WT_SIZE_CHECK_UNPACK(1, maxlen);
510 WT_RET(__wt_vunpack_uint(pp, maxlen, &pv->u.u));
511 /* FALLTHROUGH */
512 case 'u':
513 if (pv->havesize)
514 s = pv->size;
515 else if (pv->type == 'U')
516 s = (size_t)pv->u.u;
517 else
518 s = maxlen;
519 WT_SIZE_CHECK_UNPACK(s, maxlen);
520 pv->u.item.data = *pp;
521 pv->u.item.size = s;
522 *pp += s;
523 break;
524 case 'b':
525 /* Translate to maintain ordering with the sign bit. */
526 WT_SIZE_CHECK_UNPACK(1, maxlen);
527 pv->u.i = (int8_t)(*(*pp)++ - 0x80);
528 break;
529 case 'B':
530 case 't':
531 WT_SIZE_CHECK_UNPACK(1, maxlen);
532 pv->u.u = *(*pp)++;
533 break;
534 case 'h':
535 case 'i':
536 case 'l':
537 case 'q':
538 /*
539 * Check that there is at least one byte available: the
540 * low-level routines treat zero length as unchecked.
541 */
542 WT_SIZE_CHECK_UNPACK(1, maxlen);
543 WT_RET(__wt_vunpack_int(pp, maxlen, &pv->u.i));
544 break;
545 case 'H':
546 case 'I':
547 case 'L':
548 case 'Q':
549 case 'r':
550 /*
551 * Check that there is at least one byte available: the
552 * low-level routines treat zero length as unchecked.
553 */
554 WT_SIZE_CHECK_UNPACK(1, maxlen);
555 WT_RET(__wt_vunpack_uint(pp, maxlen, &pv->u.u));
556 break;
557 case 'R':
558 WT_SIZE_CHECK_UNPACK(sizeof(uint64_t), maxlen);
559 pv->u.u = *(const uint64_t *)*pp;
560 *pp += sizeof(uint64_t);
561 break;
562 default:
563 WT_RET_MSG(session, EINVAL,
564 "unknown pack-value type: %c", (int)pv->type);
565 }
566
567 return (0);
568 }
569
570 #define WT_UNPACK_PUT(session, pv, ap) do { \
571 WT_ITEM *__item; \
572 switch ((pv).type) { \
573 case 'x': \
574 break; \
575 case 's': \
576 case 'S': \
577 *va_arg(ap, const char **) = (pv).u.s; \
578 break; \
579 case 'U': \
580 case 'u': \
581 __item = va_arg(ap, WT_ITEM *); \
582 __item->data = (pv).u.item.data; \
583 __item->size = (pv).u.item.size; \
584 break; \
585 case 'b': \
586 *va_arg(ap, int8_t *) = (int8_t)(pv).u.i; \
587 break; \
588 case 'h': \
589 *va_arg(ap, int16_t *) = (short)(pv).u.i; \
590 break; \
591 case 'i': \
592 case 'l': \
593 *va_arg(ap, int32_t *) = (int32_t)(pv).u.i; \
594 break; \
595 case 'q': \
596 *va_arg(ap, int64_t *) = (pv).u.i; \
597 break; \
598 case 'B': \
599 case 't': \
600 *va_arg(ap, uint8_t *) = (uint8_t)(pv).u.u; \
601 break; \
602 case 'H': \
603 *va_arg(ap, uint16_t *) = (uint16_t)(pv).u.u; \
604 break; \
605 case 'I': \
606 case 'L': \
607 *va_arg(ap, uint32_t *) = (uint32_t)(pv).u.u; \
608 break; \
609 case 'Q': \
610 case 'r': \
611 case 'R': \
612 *va_arg(ap, uint64_t *) = (pv).u.u; \
613 break; \
614 /* User format strings have already been validated. */ \
615 WT_ILLEGAL_VALUE(session, (pv).type); \
616 } \
617 } while (0)
618
619 /*
620 * __wt_struct_packv --
621 * Pack a byte string (va_list version).
622 */
623 static inline int
__wt_struct_packv(WT_SESSION_IMPL * session,void * buffer,size_t size,const char * fmt,va_list ap)624 __wt_struct_packv(WT_SESSION_IMPL *session,
625 void *buffer, size_t size, const char *fmt, va_list ap)
626 {
627 WT_DECL_PACK_VALUE(pv);
628 WT_DECL_RET;
629 WT_PACK pack;
630 uint8_t *p, *end;
631
632 p = buffer;
633 end = p + size;
634
635 if (fmt[0] != '\0' && fmt[1] == '\0') {
636 pv.type = fmt[0];
637 WT_PACK_GET(session, pv, ap);
638 return (__pack_write(session, &pv, &p, size));
639 }
640
641 WT_RET(__pack_init(session, &pack, fmt));
642 while ((ret = __pack_next(&pack, &pv)) == 0) {
643 WT_PACK_GET(session, pv, ap);
644 WT_RET(__pack_write(session, &pv, &p, (size_t)(end - p)));
645 }
646 WT_RET_NOTFOUND_OK(ret);
647
648 /* Be paranoid - __pack_write should never overflow. */
649 WT_ASSERT(session, p <= end);
650
651 return (0);
652 }
653
654 /*
655 * __wt_struct_sizev --
656 * Calculate the size of a packed byte string (va_list version).
657 */
658 static inline int
__wt_struct_sizev(WT_SESSION_IMPL * session,size_t * sizep,const char * fmt,va_list ap)659 __wt_struct_sizev(
660 WT_SESSION_IMPL *session, size_t *sizep, const char *fmt, va_list ap)
661 {
662 WT_DECL_PACK_VALUE(pv);
663 WT_DECL_RET;
664 WT_PACK pack;
665 size_t v;
666
667 *sizep = 0;
668
669 if (fmt[0] != '\0' && fmt[1] == '\0') {
670 pv.type = fmt[0];
671 WT_PACK_GET(session, pv, ap);
672 return (__pack_size(session, &pv, sizep));
673 }
674
675 WT_RET(__pack_init(session, &pack, fmt));
676 while ((ret = __pack_next(&pack, &pv)) == 0) {
677 WT_PACK_GET(session, pv, ap);
678 WT_RET(__pack_size(session, &pv, &v));
679 *sizep += v;
680 }
681 WT_RET_NOTFOUND_OK(ret);
682
683 return (0);
684 }
685
686 /*
687 * __wt_struct_unpackv --
688 * Unpack a byte string (va_list version).
689 */
690 static inline int
__wt_struct_unpackv(WT_SESSION_IMPL * session,const void * buffer,size_t size,const char * fmt,va_list ap)691 __wt_struct_unpackv(WT_SESSION_IMPL *session,
692 const void *buffer, size_t size, const char *fmt, va_list ap)
693 {
694 WT_DECL_PACK_VALUE(pv);
695 WT_DECL_RET;
696 WT_PACK pack;
697 const uint8_t *p, *end;
698
699 p = buffer;
700 end = p + size;
701
702 if (fmt[0] != '\0' && fmt[1] == '\0') {
703 pv.type = fmt[0];
704 WT_RET(__unpack_read(session, &pv, &p, size));
705 WT_UNPACK_PUT(session, pv, ap);
706 return (0);
707 }
708
709 WT_RET(__pack_init(session, &pack, fmt));
710 while ((ret = __pack_next(&pack, &pv)) == 0) {
711 WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p)));
712 WT_UNPACK_PUT(session, pv, ap);
713 }
714 WT_RET_NOTFOUND_OK(ret);
715
716 /* Be paranoid - __pack_write should never overflow. */
717 WT_ASSERT(session, p <= end);
718
719 return (0);
720 }
721
722 /*
723 * __wt_struct_size_adjust --
724 * Adjust the size field for a packed structure.
725 *
726 * Sometimes we want to include the size as a field in a packed structure.
727 * This is done by calling __wt_struct_size with the expected format and
728 * a size of zero. Then we want to pack the structure using the final
729 * size. This function adjusts the size appropriately (taking into
730 * account the size of the final size or the size field itself).
731 */
732 static inline void
__wt_struct_size_adjust(WT_SESSION_IMPL * session,size_t * sizep)733 __wt_struct_size_adjust(WT_SESSION_IMPL *session, size_t *sizep)
734 {
735 size_t curr_size, field_size, prev_field_size;
736
737 curr_size = *sizep;
738 prev_field_size = 1;
739
740 while ((field_size = __wt_vsize_uint(curr_size)) != prev_field_size) {
741 curr_size += field_size - prev_field_size;
742 prev_field_size = field_size;
743 }
744
745 /* Make sure the field size we calculated matches the adjusted size. */
746 WT_ASSERT(session, field_size == __wt_vsize_uint(curr_size));
747
748 *sizep = curr_size;
749 }
750