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