summaryrefslogtreecommitdiffstats
path: root/usr.sbin/nsd/udb.h
blob: 3ee98648b157b7046b95d11ca10c1bf937dbf5d6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
/* udb.h - u(micro) data base, stores data and index information in mmap file.
 * By W.C.A. Wijngaards
 * Copyright 2010, NLnet Labs.
 * BSD, see LICENSE.
 */
#ifndef UDB_H
#define UDB_H
#include <assert.h>

/**
 * The micro data base UDB.
 *
 * File data.udb is mmapped and used to lookup and edit.
 * it contains a header with space-allocation-info, and a reference to the
 * base information, an object that is the entry point for the file.
 * Then it contains a lot of data and index objects.
 *
 * The space allocator is 'buddy system', 1megareas, larger get own area.
 * So worst case is 2xdata filesize (+header).  Growth semi-linear.
 * Chunks have size and type (for recovery).  Call to reserve space.
 * Call to 'realloc-in-place', if space permits.
 *
 * Usually you want a record-type and its indexes (sorted) to be stored in
 * the file.  This is a table (named by string).  The record is opaque
 * data.
 *
 * To be able to use pointers in the mmapped file, there is conversion of
 * relative-pointers(to file base) to system-pointers.
 *
 * If an item is moved its internal pointers need to be recalculated.
 * Thus a recordtype (that has internal pointers) must provide a routine.
 * Structures that are 'on-disk', are denoted with _d. Except rel_ptr which
 * is also on-disk.
 *
 * About 64-bit trouble.  The pointer-size which which the application is
 * compiled determines the file layout, because this makes it perform well
 * in a mmap.  It could in theory be converted if you really wanted to.
 * Nonpointer data is best stored as a fixed bitsize (uint8, 16, 32, 64).
 */
typedef struct udb_base udb_base;
typedef struct udb_alloc udb_alloc;

/** these checks are very slow, disabled by default */
#if 0
/** perform extra checks (when --enable-checking is used) */
#ifndef NDEBUG
#define UDB_CHECK 1
#endif
#endif

/** pointers are stored like this */
typedef uint64_t udb_void;

/** convert relptr to usable pointer */
#define UDB_REL(base, relptr) ((void*)((char*)(base) + (relptr)))
/** from system pointer to relative pointer */
#define UDB_SYSTOREL(base, ptr) ((udb_void)((char*)(ptr) - (char*)(base)))

/** MAX 2**x exponent of alloced chunks, for 1Mbytes.  The smallest
 * chunk is 16bytes (8preamble+8data), so 0-3 is unused. */
#define UDB_ALLOC_CHUNKS_MAX 20
/** size of areas that are subdivided */
#define UDB_ALLOC_CHUNK_SIZE ((uint64_t)1<<UDB_ALLOC_CHUNKS_MAX)
/** the minimum alloc in exp, 2**x.  32bytes because of chunk_free_d size (8aligned) */
#define UDB_ALLOC_CHUNK_MINEXP 5
/** size of minimum alloc */
#define UDB_ALLOC_CHUNK_MINSIZE ((uint64_t)1<<UDB_ALLOC_CHUNK_MINEXP)
/** exp size used to mark the header (cannot be reallocated) */
#define UDB_EXP_HEADER 0
/** exp size used to mark XL(extralarge) allocations (in whole mbs) */
#define UDB_EXP_XL 1

typedef struct udb_ptr udb_ptr;
/**
 * This structure is there for when you want to have a pointer into
 * the mmap-ed file.  It is kept track of.  Set it to NULL to unlink it.
 * For pointers to the mmap-ed file from within the mmap-ed file, use the
 * rel_pre construct below.
 */
struct udb_ptr {
	/** the data segment it points to (relative file offset) */
	uint64_t data;
	/** pointer to the base pointer (for convenience) */
	void** base;
	/** prev in udb_ptr list for this data segment */
	udb_ptr* prev;
	/** next in udb_ptr list for this data segment */
	udb_ptr* next;
};

typedef struct udb_rel_ptr udb_rel_ptr;
/**
 * A relative pointer that keeps track of the list of pointers,
 * so that it can be reallocated.
 */
struct udb_rel_ptr {
	/** the relative pointer to the data itself (subtract chunk_d size
	 * to get the chunk_d type, this is for usage speed in dereferencing
	 * to the userdata). */
	udb_void data;
	/** udb_rel_ptr* prev in relptr list */
	udb_void prev;
	/** udb_rel_ptr* next in relptr list */
	udb_void next;
};

/** 
 * This is the routine that is called for every relptr
 * @param base: the baseptr for REL.
 * @param p: the relptr, a real pointer to it.
 * @param arg: user argument.
 */
typedef void udb_walk_relptr_cb(void*, udb_rel_ptr*, void*);

/** 
 * This routine calls the callback for every relptr in a datablock
 * params in order:
 * base: the baseptr for REL macro.
 * warg: the walkfunc user argument.
 * t: the type of the chunk.
 * d: pointer to the data part of the chunk (real pointer).
 * s: max size of the data part.
 * cb: the callback to call for every element.
 * arg: user argument to pass to the callback.
 */
typedef void udb_walk_relptr_func(void*, void*, uint8_t, void*, uint64_t,
	udb_walk_relptr_cb*, void*);

/** What sort of salvage should be performed by alloc */
enum udb_dirty_alloc {
	udb_dirty_clean = 0, /* all clean */
	udb_dirty_fl,        /* allocs, freelists are messed up */
	udb_dirty_fsize,     /* file size and fsize are messed up */
	udb_dirty_compact    /* allocs, freelists and relptrs are messed up */
};

typedef struct udb_glob_d udb_glob_d;
/**
 * The UDB global data for a file.  This structure is mmapped.
 * Make sure it has no structure-padding problems.
 */
struct udb_glob_d {
	/** size of header in the file (offset to the first alloced chunk) */
	uint64_t hsize;
	/** version number of this file */
	uint8_t version;
	/** was the file cleanly closed, 0 is not clean, 1 is clean */
	uint8_t clean_close;
	/** an allocation operation was in progress, file needs to be salvaged
	 * type enum udb_dirty_alloc */
	uint8_t dirty_alloc;
	/** user flags */
	uint8_t userflags;
	/** padding to 8-bytes alignment */
	uint8_t pad1[4];
	/** size to mmap */
	uint64_t fsize;
	/** chunk move rollback info: oldchunk (0 is nothing).
	 * volatile because these values prevent dataloss, they need to be
	 * written immediately. */
	volatile udb_void rb_old;
	/** chunk move rollback info: newchunk (0 is nothing) */
	volatile udb_void rb_new;
	/** size of move rollback chunks */
	volatile uint64_t rb_size;
	/** segment of move rollback, for an XL chunk that overlaps. */
	volatile uint64_t rb_seg;
	/** linked list for content-listing, 0 if empty;
	 * this pointer is unused; and could be removed if the database
	 * format is modified or updated. */
	udb_rel_ptr content_list;
	/** user global data pointer */
	udb_rel_ptr user_global;
};

/**
 * The UDB database file.  Contains all the data
 */
struct udb_base {
	/** name of the file, alloced */
	char* fname;

	/** mmap base pointer (or NULL) */
	void* base;
	/** size of mmap */
	size_t base_size;
	/** fd of mmap (if -1, closed). */
	int fd;

	/** space allocator that is used for this base */
	udb_alloc* alloc;
	/** real pointer to the global data in the file */
	udb_glob_d* glob_data;

	/** store all linked udb_ptrs in this table, by hash(offset).
	 * then a linked list of ptrs (all that match the hash).
	 * this avoids buckets, and thus memory allocation. */
	udb_ptr** ram_hash;
	/** size of the current udb_ptr hashtable array */
	size_t ram_size;
	/** mask for the curren udb_ptr hashtable lookups */
	int ram_mask;
	/** number of ptrs in ram, used to decide when to grow */
	size_t ram_num;
	/** for relocation, this walks through all relptrs in chunk */
	udb_walk_relptr_func* walkfunc;
	/** user data for walkfunc */
	void* walkarg;

	/** compaction is inhibited */
	int inhibit_compact;
	/** compaction is useful; deletions performed. */
	int useful_compact;
};

typedef enum udb_chunk_type udb_chunk_type;
/** chunk type enum, setting these types help recovery and debug */
enum udb_chunk_type {
	udb_chunk_type_free = 0,
	udb_chunk_type_data, /* alloced data */
	udb_chunk_type_index,
	udb_chunk_type_radtree,
	udb_chunk_type_radnode,
	udb_chunk_type_radarray,
	udb_chunk_type_zone,
	udb_chunk_type_domain,
	udb_chunk_type_rrset,
	udb_chunk_type_rr,
	udb_chunk_type_task,
	udb_chunk_type_internal
};

typedef struct udb_chunk_d udb_chunk_d;
/**
 * UDB chunk info (prepended for every allocated chunk).
 * The chunks are in doublelinkedlists per size.
 * At the end of the chunk another exp uint8 is stored (to walk backwards).
 * 17 bytes overhead, datasize for 32byte chunk is 15.
 */
struct udb_chunk_d {
	/** the size of this chunk (i.e. 2**x) */
	uint8_t exp;
	/** type for this chunk (enum chunktype; free, data or index) */
	uint8_t type;
	/** flags for this chunk */
	uint8_t flags;
	/** padding onto 8-alignment */
	uint8_t pad[5];
	/** udb_rel_ptr* first in list of rel-ptrs that point back here 
	 * In the free chunk this is the previous pointer. */
	udb_void ptrlist;
	/* user data space starts here, 64-bit aligned */
	uint8_t data[0];
	/* last octet: exp of chunk */
};

typedef struct udb_free_chunk_d udb_free_chunk_d;
/**
 * A free chunk.  Same start as the udb_chunk_d. minsize is 32 bytes.
 */
struct udb_free_chunk_d {
	/** the size of this chunk (i.e. 2**x) */
	uint8_t exp;
	/** type for this chunk (enum chunktype; free, data or index) */
	uint8_t type;
	/** flags for this chunk */
	uint8_t flags;
	/** padding onto 8-alignment */
	uint8_t pad[5];
	/** udb_chunk_d* prev of free list for this size */
	udb_void prev;
	/** udb_chunk_d* next of free list for this size */
	udb_void next;
	/* empty stuff */
	/* last octet: exp of chunk */
};

typedef struct udb_xl_chunk_d udb_xl_chunk_d;
/**
 * an Extra Large (XL) chunk.  Same start as the udb_chunk_d.  Allocated in whole
 * MAX_CHUNK_SIZE parts, whole megabytes.  overhead is 5x8=40 bytes.
 */
struct udb_xl_chunk_d {
	/** the size of this chunk (i.e. 2**x): special XL value */
	uint8_t exp;
	/** type for this chunk (enum chunktype; free, data or index) */
	uint8_t type;
	/** flags for this chunk */
	uint8_t flags;
	/** padding onto 8-alignment */
	uint8_t pad[5];
	/** udb_rel_ptr* first in list of rel-ptrs that point back here 
	 * In the free chunk this is the previous pointer. */
	udb_void ptrlist;
	/** size of this chunk in bytes */
	uint64_t size;
	/** data of the XL chunk */
	uint8_t data[0];
	/* uint64_t endsize: before last octet the size again. */
	/* uint8_t pad[7]: padding to make last octet last. */
	/* last octet: exp of chunk: special XL value */
};

typedef struct udb_alloc_d udb_alloc_d;
/**
 * UDB alloc info on disk.
 */
struct udb_alloc_d {
	/** stats: number of data bytes allocated, sum of sizes passed to alloc */
	uint64_t stat_data;
	/** stats: number of bytes in free chunks, sum of their 2**x size */
	uint64_t stat_free;
	/** stats: number of bytes in alloced chunks, sum of their 2**x size */
	uint64_t stat_alloc;
	/** offset to create next chunk at. can be before file-end, or be 
	 * fsize, volatile because it is used as a 'commit', and thus we want
	 * this to be written to memory (and thus disk) immediately. */
	volatile uint64_t nextgrow;
	/** fixed size array the points to the 2**x size chunks in the file,
	 * This is the start of the doublelinked list, ptr to udb_free_chunk_d.
	 * array starts at UDB_ALLOC_CHUNK_MINEXP entry as [0]. */
	udb_void free[UDB_ALLOC_CHUNKS_MAX-UDB_ALLOC_CHUNK_MINEXP+1];
};

/**
 * The UDB space allocator.  Assigns space in the file.
 */
struct udb_alloc {
	/** the base this is part of */
	udb_base* udb;
	/** real pointer to space allocation info on disk; fixedsize struct */
	udb_alloc_d* disk;
};

/** 
 * file header length, the file start with
 * 64bit: magic number to identify file (and prevent stupid mistakes)
 * globdata: global data. Fixed size segment. (starts with size uint64)
 * allocdata: alloc global data. Fixed size segment.
 * size and 0 byte: end marker for reverse search.
 */
#define UDB_HEADER_SIZE (sizeof(uint64_t)+sizeof(udb_glob_d)+ \
	sizeof(udb_alloc_d)+sizeof(uint64_t)*2)
/** magic string that starts an UDB file, uint64_t, note first byte=0, to mark
 * header start as a chunk. */
#define UDB_MAGIC (((uint64_t)'u'<<48)|((uint64_t)'d'<<40)|((uint64_t)'b' \
	<<32)|((uint64_t)'v'<<24)|((uint64_t)'0'<<16)|((uint64_t)'b'<<8))

/* UDB BASE */
/**
 * Create udb base structure and attempt to read the file.
 * @param fname: file name.
 * @param walkfunc: function to walk through relptrs in chunk.
 * @param arg: user argument to pass to walkfunc
 * @return base structure or NULL on failure.
 */
udb_base* udb_base_create_read(const char* fname, udb_walk_relptr_func walkfunc,
	void* arg);

/**
 * Create udb base structure and create a new file.
 * @param fname: file name.
 * @param walkfunc: function to walk through relptrs in chunk.
 * @param arg: user argument to pass to walkfunc
 * @return base structure or NULL on failure.
 */
udb_base* udb_base_create_new(const char* fname, udb_walk_relptr_func walkfunc,
	void* arg);

/**
 * Create udb from (O_RDWR) fd.
 * @param fname: file name.
 * @param fd: file descriptor.
 * @param walkfunc: function to walk through relptrs in chunk.
 * @param arg: user argument to pass to walkfunc
 * @return base structure or NULL on failure.
 */
udb_base* udb_base_create_fd(const char* fname, int fd,
	udb_walk_relptr_func walkfunc, void* arg);

/**
 * Properly close the UDB base file.  Separate from delete so the
 * most important bits (write to disk, sockets) can be done first.
 * @param udb: the udb.
 */
void udb_base_close(udb_base* udb);

/**
 * Free the data structure (and close if not already) the udb.
 * @param udb: the udb.
 */
void udb_base_free(udb_base* udb);

/**
 * Free the udb, but keep mmap mapped for others.
 * @param udb: the udb.
 */
void udb_base_free_keep_mmap(udb_base* udb);

/**
 * Sync the mmap.
 * @param udb: the udb.
 * @param wait: if true, the call blocks until synced.
 */
void udb_base_sync(udb_base* udb, int wait);

/**
 * The mmap size is updated to reflect changes by another process.
 * @param udb: the udb.
 */
void udb_base_remap_process(udb_base* udb);

/**
 * get the user data (relative) pointer.
 * @param udb: the udb.
 * @return the userdata relative pointer, 0 means nothing.
 */
udb_rel_ptr* udb_base_get_userdata(udb_base* udb);

/**
 * Set the user data (relative) pointer.
 * @param udb: the udb.
 * @param user: user data. offset-pointer (or 0).
 */
void udb_base_set_userdata(udb_base* udb, udb_void user);

/** 
 * Set the user flags (to any value, uint8). 
 * @param udb: the udb.
 * @param v: new value.
 */
void udb_base_set_userflags(udb_base* udb, uint8_t v);

/** 
 * Get the user flags. 
 * @param udb: the udb.
 * @param v: new value.
 */
uint8_t udb_base_get_userflags(udb_base* udb);

/**
 * Not for users of udb_base, but for udb_ptr.
 * Link in a new ptr that references a data segment.
 * @param udb: the udb.
 * @param ptr: to link in.
 */
void udb_base_link_ptr(udb_base* udb, udb_ptr* ptr);

/**
 * Not for users of udb_base, but for udb_ptr.
 * Unlink a ptr that references a data segment.
 * @param udb: the udb.
 * @param ptr: to unlink.
 */
void udb_base_unlink_ptr(udb_base* udb, udb_ptr* ptr);

/* UDB ALLOC */
/**
 * Utility for alloc, find 2**x size that is bigger than the given size.
 * Does not work for amount==0.
 * @param amount: amount of memory.
 * @return x; the exponent where 2**x >= amount.
 */
int udb_exp_size(uint64_t amount);

/**
 * Utility for alloc, what is the size that the current offset supports
 * as a maximum 2**x chunk.
 * Does not work for offset = 0 (result is infinite).
 * @param offset: the offset into the memory region.
 * @return maximum exponent where 2**x is fits the offset, thus
 * 	offset % (2**x) == 0 and x cannot be larger.
 */
int udb_exp_offset(uint64_t offset);

/**
 * Convert pointer to the data part to a pointer to the base of the chunk.
 * @param data: data part.
 * @return pointer to the base of the chunk.
 */
udb_void chunk_from_dataptr_ext(udb_void data);

/**
 * Create empty UDB allocate structure to write to disk to initialize file.
 * @param a: allocation structure to initialize.  system pointer.
 */
void udb_alloc_init_new(udb_alloc_d* a);

/**
 * Create new udb allocator, with specific data on disk
 * @param udb: the udb.
 * @param disk: disk data.
 * @return udb allocator or NULL on (malloc) failure.
 */
udb_alloc* udb_alloc_create(udb_base* udb, udb_alloc_d* disk);

/**
 * Free the udb allocator from memory.
 * @param alloc: the udb space allocator.
 */
void udb_alloc_delete(udb_alloc* alloc);

/**
 * Allocate space on the disk.
 * This may involve closing and reopening the mmap.
 * @param alloc: the udb space allocator.
 * @param sz: size you want to use.
 * @return relative pointer (or 0 on alloc failure).
 */
udb_void udb_alloc_space(udb_alloc* alloc, size_t sz);

/**
 * Allocate space on disk, give already the data you want there.
 * This may involve closing and reopening the mmap.
 * @param alloc: the udb space allocator.
 * @param d: data you want there (system pointer).
 * @param sz: size you want to use.
 * @return relative pointer (or 0 on alloc failure).
 */
udb_void udb_alloc_init(udb_alloc* alloc, void* d, size_t sz);

/**
 * free allocated space.  It may shrink the file.
 * This may involve closing and reopening the mmap.
 * @param alloc: the udb space allocator.
 * @param r: relative pointer to data you want to free.
 * @param sz: the size of the data you stop using.
 * @return false if the free failed, it failed the close and mmap.
 */
int udb_alloc_free(udb_alloc* alloc, udb_void r, size_t sz);

/**
 * realloc an existing allocated space.  It may grow the file.
 * This may involve closing and reopening the mmap.
 * It could also use the existing space where it is now.
 * @param alloc: the udb space allocator.
 * @param r: relative pointer to data you want to realloc.
 *	if 0 then this is alloc_space(), and osz is ignored.
 * @param osz: the old size of the data.
 * @param sz: the size of the data you want to get.
 *	if this is 0 then a free() is done, but please do it directly,
 *	as you then get a returnvalue (file errors).
 * @return relative pointer (0 on alloc failure, same if not moved).
 */
udb_void udb_alloc_realloc(udb_alloc* alloc, udb_void r, size_t osz,
	size_t sz);

/**
 * Prepare for a lot of new entries.  Grow space for that.
 * This can involve closing and reopening the mmap.
 * This space (if large) is going to be released on next free() or close().
 * @param alloc: the udb space allocator.
 * @param sz: size of the entries.
 * @param num: number of entries.
 * @return false on failure to grow or re-mmap.
 */
int udb_alloc_grow(udb_alloc* alloc, size_t sz, size_t num);

/** 
 * attempt to compact the data and move free space to the end
 * can shrink the db, which calls sync on the db (for portability).
 * @param udb: the udb base.
 * @return 0 on failure (to remap the (possibly) changed udb base).
 */
int udb_compact(udb_base* udb);

/** 
 * set the udb to inhibit or uninhibit compaction.  Does not perform
 * the compaction itself if enabled, for that call udb_compact.
 * @param udb: the udb base
 * @param inhibit: 0 or 1.
 */
void udb_compact_inhibited(udb_base* udb, int inhibit);

/**
 * Set the alloc type for a newly alloced piece of data
 * @param alloc: the udb space allocator.
 * @param r: relativeptr to the data.
 * @param tp: the type of that block.
 */
void udb_alloc_set_type(udb_alloc* alloc, udb_void r, udb_chunk_type tp);

/**
 * See if a pointer could be valid (it points within valid space),
 * for the given type side.  For debug checks.
 * @param udb: the udb
 * @param to: the ptr (offset).
 * @param destsize: the size_of of the destination of the pointer.
 * @return true if it points to a valid region.
 */
int udb_valid_offset(udb_base* udb, udb_void to, size_t destsize);

/**
 * See if a pointer is valid (it points to a chunk).  For debug checks.
 * @param udb: the udb.
 * @param to: the ptr (offset).
 * @return true if it points to the start of a chunks data region.
 */
int udb_valid_dataptr(udb_base* udb, udb_void to);

/**
 * See if a pointer is on the relptrlist for dataptr.  For debug checks.
 * @param udb: the udb.
 * @param rptr: the rel_ptr (offset).
 * @param to: dataptr of the chunk on which ptrlist the rptr is searched.
 * @return true if rptr is valid and on the ptrlist.
 */
int udb_valid_rptr(udb_base* udb, udb_void rptr, udb_void to);

/*** UDB_REL_PTR ***/
/** 
 * Init a new UDB rel ptr at NULL.
 * @param ptr: sysptr, becomes inited.
 */
void udb_rel_ptr_init(udb_rel_ptr* ptr);

/** 
 * Unlink a UDB rel ptr.
 * @param base: the udb base
 * @param ptr: sysptr, unlinked
 */
void udb_rel_ptr_unlink(void* base, udb_rel_ptr* ptr);

/**
 * Link a UDB rel ptr to a new chunk
 * @param base: the udb base
 * @param ptr: sysptr, linked to new value.
 * @param to: the data to point to (relative ptr).
 */
void udb_rel_ptr_link(void* base, udb_rel_ptr* ptr, udb_void to);

/**
 * Change rel ptr to a new value (point to another record)
 * @param base: the udb base
 * @param ptr: sysptr, points to new value.
 * @param to: the data to point to (relative ptr).
 */
void udb_rel_ptr_set(void* base, udb_rel_ptr* ptr, udb_void to);

/**
 * A chunk has moved and now edit all the relptrs in list to fix them up
 * @param base: the udb base
 * @param list: start of the ptr list
 * @param to: where the chunk has moved to relptr to its userdata.
 */
void udb_rel_ptr_edit(void* base, udb_void list, udb_void to);

/**
 * Get system pointer.  Assumes there is a variable named 'base'
 * that points to the udb base.
 * @param ptr: the relative pointer (a sysptr to it).
 * @return void* to the data.
 */
#define UDB_SYSPTR(ptr) UDB_REL(base, (ptr)->data)

/** get sys ptr for char* string */
#define UDB_CHAR(ptr) ((char*)UDB_REL(base, ptr))
/** get sys ptr for udb_rel_ptr */
#define UDB_REL_PTR(ptr) ((udb_rel_ptr*)UDB_REL(base, ptr))
/** get sys ptr for udb_glob_d */
#define UDB_GLOB(ptr) ((udb_glob_d*)UDB_REL(base, ptr))
/** get sys ptr for udb_chunk_d */
#define UDB_CHUNK(ptr) ((udb_chunk_d*)UDB_REL(base, ptr))
/** get sys ptr for udb_free_chunk_d */
#define UDB_FREE_CHUNK(ptr) ((udb_free_chunk_d*)UDB_REL(base, ptr))
/** get sys ptr for udb_xl_chunk_d */
#define UDB_XL_CHUNK(ptr) ((udb_xl_chunk_d*)UDB_REL(base, ptr))

/* udb_ptr */
/**
 * Initialize an udb ptr.  Set to NULL.  (and thus not linked can be deleted).
 * You MUST set it to 0 before you stop using the ptr.
 * @param ptr: the ptr to initialise (caller has allocated it).
 * @param udb: the udb base to link it to.
 */
void udb_ptr_init(udb_ptr* ptr, udb_base* udb);

/**
 * Set udp ptr to a new value.  If set to NULL you can delete it.
 * @param ptr: the ptr.
 * @param udb: the udb base to link up with that data segment's administration.
 * @param newval: new value to point to (udb_void relative file offset to data).
 */
void udb_ptr_set(udb_ptr* ptr, udb_base* udb, udb_void newval);

/** dereference udb_ptr */
#define UDB_PTR(ptr) (UDB_REL(*((ptr)->base), (ptr)->data))

/**
 * Ease of use udb ptr, allocate space and return ptr to it
 * You MUST udb_ptr_set it to 0 before you stop using the ptr.
 * @param base: udb base to use.
 * @param ptr: ptr is overwritten, can be uninitialised.
 * @param type: type of the allocation.
 * 	You need a special type if the block contains udb_rel_ptr's.
 * 	You can use udb_type_data for plain data.
 * @param sz: amount to allocate.
 * @return 0 on alloc failure.
 */
int udb_ptr_alloc_space(udb_ptr* ptr, udb_base* udb, udb_chunk_type type,
	size_t sz);

/**
 * Ease of use udb ptr, free space and set ptr to NULL (to it can be deleted).
 * The space is freed on disk.
 * @param ptr: the ptr.
 * @param udb: udb base.
 * @param sz: the size of the data you stop using.
 */
void udb_ptr_free_space(udb_ptr* ptr, udb_base* udb, size_t sz);

/**
 * Get pointer to the data of the ptr.  or use a macro to cast UDB_PTR to
 * the type of your structure(.._d)
 */
static inline uint8_t* udb_ptr_data(udb_ptr* ptr) {
	return (uint8_t*)UDB_PTR(ptr);
}

/**
 * See if udb ptr is null
 */
static inline int udb_ptr_is_null(udb_ptr* ptr) {
	return (ptr->data == 0);
}

/**
 * Get the type of a udb_ptr chunk.
 * @param ptr: udb pointer
 * @return type of chunk */
udb_chunk_type udb_ptr_get_type(udb_ptr* ptr);

/** Ease of use, create new pointer to destination relptr
 * You MUST udb_ptr_set it to 0 before you stop using the ptr. */
static inline void udb_ptr_new(udb_ptr* ptr, udb_base* udb, udb_rel_ptr* d) {
	udb_ptr_init(ptr, udb);
	udb_ptr_set(ptr, udb, d->data);
}

/** Ease of use.  Stop using this ptr */
static inline void udb_ptr_unlink(udb_ptr* ptr, udb_base* udb) {
	if(ptr->data)
		udb_base_unlink_ptr(udb, ptr);
}

/* Ease of use.  Assign rptr from rptr */
static inline void udb_rptr_set_rptr(udb_rel_ptr* dest, udb_base* udb,
	udb_rel_ptr* p) {
#ifdef UDB_CHECK
	if(dest->data) { assert(udb_valid_rptr(udb,
		UDB_SYSTOREL(udb->base, dest), dest->data)); }
	if(p->data) { assert(udb_valid_rptr(udb,
		UDB_SYSTOREL(udb->base, p), p->data)); }
#endif
	udb_rel_ptr_set(udb->base, dest, p->data);
}

/* Ease of use.  Assign rptr from ptr */
static inline void udb_rptr_set_ptr(udb_rel_ptr* dest, udb_base* udb,
	udb_ptr* p) {
#ifdef UDB_CHECK
	if(dest->data) { assert(udb_valid_rptr(udb,
		UDB_SYSTOREL(udb->base, dest), dest->data)); }
	if(p->data) { assert(udb_valid_dataptr(udb, p->data)); }
#endif
	udb_rel_ptr_set(udb->base, dest, p->data);
}

/* Ease of use.  Assign ptr from rptr */
static inline void udb_ptr_set_rptr(udb_ptr* dest, udb_base* udb,
	udb_rel_ptr* p) {
#ifdef UDB_CHECK
	if(p->data) { assert(udb_valid_rptr(udb,
		UDB_SYSTOREL(udb->base, p), p->data)); }
#endif
	udb_ptr_set(dest, udb, p->data);
}

/* Ease of use.  Assign ptr from ptr */
static inline void udb_ptr_set_ptr(udb_ptr* dest, udb_base* udb, udb_ptr* p) {
	udb_ptr_set(dest, udb, p->data);
}

/* Ease of use, zero rptr.  You use this to zero an existing pointer.
 * A new rptr should be rel_ptr_init-ed before it is taken into use. */
static inline void udb_rptr_zero(udb_rel_ptr* dest, udb_base* udb) {
#ifdef UDB_CHECK
	if(dest->data) { assert(udb_valid_rptr(udb,
		UDB_SYSTOREL(udb->base, dest), dest->data)); }
#endif
	udb_rel_ptr_set(udb->base, dest, 0);
}

/* Ease of use, zero ptr */
static inline void udb_ptr_zero(udb_ptr* dest, udb_base* udb) {
	udb_ptr_set(dest, udb, 0);
}

/** ease of use, delete memory pointed at by relptr */
static inline void udb_rel_ptr_free_space(udb_rel_ptr* ptr, udb_base* udb,
	size_t sz) {
	udb_void d = ptr->data;
#ifdef UDB_CHECK
	if(d) { assert(udb_valid_rptr(udb, UDB_SYSTOREL(udb->base, ptr), d)); }
#endif
	udb_rel_ptr_set(udb->base, ptr, 0);
	udb_alloc_free(udb->alloc, d, sz);
}

#endif /* UDB_H */