maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
mdata.h
Go to the documentation of this file.
1
2#ifndef MDATA_H
3#define MDATA_H
4
12
13#ifndef MDATA_TRACE_LVL
14# define MDATA_TRACE_LVL 0
15#endif /* !MDATA_TRACE_LVL */
16
17#ifndef MDATA_TABLE_KEY_SZ_MAX
18# define MDATA_TABLE_KEY_SZ_MAX 8
19#endif /* !MDATA_TABLE_KEY_SZ_MAX */
20
25
34#define MDATA_VECTOR_FLAG_REFCOUNT 0x01
35
36#define MDATA_VECTOR_FLAG_IS_LOCKED 0x02
37
38#ifndef MDATA_VECTOR_INIT_STEP_SZ
43# define MDATA_VECTOR_INIT_STEP_SZ 10
44#endif /* !MDATA_VECTOR_INIT_STEP_SZ */
45
47
54
55#define MDATA_STRPOOL_FLAG_IS_LOCKED 0x01
56
57#define MDATA_STRPOOL_FLAG_DEDUPE 0x02
58
59typedef size_t mdata_strpool_idx_t;
60
69 uint8_t flags;
70 MAUG_MHANDLE str_h;
71 char* str_p;
72 size_t str_sz;
73 size_t str_sz_max;
74};
75 /* mdata_strpool */
77
83
94 uint8_t flags;
96 MAUG_MHANDLE data_h;
98 uint8_t* data_bytes;
100 size_t ct_max;
102 size_t ct;
104 size_t ct_step;
109 size_t item_sz;
111 ssize_t locks;
112};
113 /* mdata_vector */
115
120
122 char string[MDATA_TABLE_KEY_SZ_MAX + 1];
123 size_t string_sz;
124 uint32_t hash;
125};
126
128 volatile uint16_t flags;
129 struct MDATA_VECTOR data_cols[2];
130 size_t key_sz;
131};
132
134
139
144 struct MDATA_STRPOOL* sp, mdata_strpool_idx_t idx );
145
146mdata_strpool_idx_t mdata_strpool_find(
147 struct MDATA_STRPOOL* sp, const char* str, size_t str_sz );
148
154 struct MDATA_STRPOOL* sp, mdata_strpool_idx_t idx );
155
156mdata_strpool_idx_t mdata_strpool_append(
157 struct MDATA_STRPOOL* sp, const char* str, size_t str_sz, uint8_t flags );
158
159MERROR_RETVAL mdata_strpool_remove(
160 struct MDATA_STRPOOL* sp, mdata_strpool_idx_t idx );
161
162MERROR_RETVAL mdata_strpool_alloc(
163 struct MDATA_STRPOOL* sp, size_t alloc_sz );
164
165void mdata_strpool_free( struct MDATA_STRPOOL* sp );
166
168
173
188 struct MDATA_VECTOR* v, const void* item, size_t item_sz );
189
190ssize_t mdata_vector_insert(
191 struct MDATA_VECTOR* v, const void* item, ssize_t idx, size_t item_sz );
192
199
208void* mdata_vector_get_void( const struct MDATA_VECTOR* v, size_t idx );
209
210MERROR_RETVAL mdata_vector_copy(
211 struct MDATA_VECTOR* v_dest, struct MDATA_VECTOR* v_src );
212
218 struct MDATA_VECTOR* v, size_t item_sz, size_t item_ct_init );
219
220void mdata_vector_free( struct MDATA_VECTOR* v );
221 /* mdata_vector */
223
224uint32_t mdata_hash( const char* token, size_t token_sz );
225
230
231typedef MERROR_RETVAL (*mdata_table_iter_t)(
232 const struct MDATA_TABLE_KEY* key, void* data, size_t data_sz,
233 void* cb_data, size_t cb_data_sz, size_t idx );
234
235MERROR_RETVAL mdata_table_lock( struct MDATA_TABLE* t );
236
237MERROR_RETVAL mdata_table_unlock( struct MDATA_TABLE* t );
238
239MERROR_RETVAL mdata_table_iter(
240 struct MDATA_TABLE* t,
241 mdata_table_iter_t cb, void* cb_data, size_t cb_data_sz );
242
243MERROR_RETVAL mdata_table_set(
244 struct MDATA_TABLE* t, const char* key,
245 void* value, size_t value_sz );
246
247MERROR_RETVAL mdata_table_unset(
248 struct MDATA_TABLE* t, const char* key );
249
250void* mdata_table_get_void( const struct MDATA_TABLE* t, const char* key );
251
252void* mdata_table_hash_get_void(
253 struct MDATA_TABLE* t, uint32_t key_hash, size_t key_sz );
254
255void mdata_table_free( struct MDATA_TABLE* t );
256
258
259#if MDATA_TRACE_LVL > 0
260# define mdata_debug_printf( fmt, ... ) \
261 debug_printf( MDATA_TRACE_LVL, fmt, __VA_ARGS__ );
262#else
263# define mdata_debug_printf( fmt, ... )
264#endif /* MDATA_TRACE_LVL */
265
270
271#define mdata_strpool_sz( sp ) ((sp)->str_sz_max)
272
273#define mdata_strpool_is_locked( sp ) \
274 (MDATA_STRPOOL_FLAG_IS_LOCKED == \
275 (MDATA_STRPOOL_FLAG_IS_LOCKED & (sp)->flags))
276
277#define mdata_strpool_lock( sp ) \
278 mdata_debug_printf( "locking strpool %p...", sp ); \
279 if( NULL != (sp)->str_p ) { \
280 error_printf( "str_p not null! double lock?" ); \
281 retval = MERROR_ALLOC; \
282 goto cleanup; \
283 } \
284 maug_mlock( (sp)->str_h, (sp)->str_p ); \
285 maug_cleanup_if_null_lock( char*, (sp)->str_p ); \
286 (sp)->flags |= MDATA_STRPOOL_FLAG_IS_LOCKED;
287
288#define mdata_strpool_unlock( sp ) \
289 mdata_debug_printf( "unlocking strpool %p...", sp ); \
290 if( NULL != (sp)->str_p ) { \
291 maug_munlock( (sp)->str_h, (sp)->str_p ); \
292 (sp)->flags &= ~MDATA_STRPOOL_FLAG_IS_LOCKED; \
293 }
294
295#define mdata_strpool_get( sp, idx ) \
296 ((idx >= 1 && idx < (sp)->str_sz) ? &((sp)->str_p[idx]) : NULL)
297
298#define mdata_strpool_get_sz( sp, idx ) \
299 ((idx >= sizeof( size_t ) && idx < (sp)->str_sz) ? \
300 (size_t)(*(&((sp)->str_p[idx - sizeof( size_t )]))) : 0)
301
302#define mdata_strpool_padding( str_sz ) \
303 (sizeof( size_t ) - ((str_sz + 1 /* NULL */) % sizeof( size_t )))
304 /* mdata_strpool */
306
311
320#define mdata_vector_lock( v ) \
321 if( (MAUG_MHANDLE)NULL == (v)->data_h && NULL == (v)->data_bytes ) { \
322 mdata_debug_printf( "locking empty vector..." ); \
323 (v)->flags |= MDATA_VECTOR_FLAG_IS_LOCKED; \
324 } else if( \
325 mdata_vector_get_flag( v, MDATA_VECTOR_FLAG_REFCOUNT ) && \
326 0 < (v)->locks \
327 ) { \
328 (v)->locks++; \
329 mdata_debug_printf( "vector " #v " locks: " SSIZE_T_FMT, (v)->locks ); \
330 } else { \
331 /* assert( !mdata_vector_is_locked( v ) ); */ \
332 if( mdata_vector_is_locked( v ) ) { \
333 error_printf( "attempting to double-lock vector!" ); \
334 retval = MERROR_OVERFLOW; \
335 goto cleanup; \
336 } \
337 if( (MAUG_MHANDLE)NULL == (v)->data_h ) { \
338 error_printf( "invalid data handle!" ); \
339 retval = MERROR_ALLOC; \
340 goto cleanup; \
341 } \
342 maug_mlock( (v)->data_h, (v)->data_bytes ); \
343 maug_cleanup_if_null_lock( uint8_t*, (v)->data_bytes ); \
344 (v)->flags |= MDATA_VECTOR_FLAG_IS_LOCKED; \
345 mdata_debug_printf( "locked vector " #v ); \
346 }
347
353#define mdata_vector_unlock( v ) \
354 if( (MAUG_MHANDLE)NULL == (v)->data_h && NULL == (v)->data_bytes ) { \
355 mdata_debug_printf( "locking empty vector..." ); \
356 (v)->flags &= ~MDATA_VECTOR_FLAG_IS_LOCKED; \
357 } else { \
358 if( \
359 mdata_vector_get_flag( v, MDATA_VECTOR_FLAG_REFCOUNT ) && \
360 0 < (v)->locks \
361 ) { \
362 (v)->locks--; \
363 mdata_debug_printf( "vector " #v " locks: " SSIZE_T_FMT, \
364 (v)->locks ); \
365 } \
366 if( 0 == (v)->locks && NULL != (v)->data_bytes ) { \
367 assert( mdata_vector_is_locked( v ) ); \
368 maug_munlock( (v)->data_h, (v)->data_bytes ); \
369 (v)->flags &= ~MDATA_VECTOR_FLAG_IS_LOCKED; \
370 mdata_debug_printf( "unlocked vector " #v ); \
371 } \
372 }
373
374#define mdata_vector_get( v, idx, type ) \
375 ((type*)mdata_vector_get_void( v, idx ))
376
377#define mdata_vector_get_last( v, type ) \
378 (0 < mdata_vector_ct( v ) ? \
379 ((type*)mdata_vector_get_void( v, \
380 mdata_vector_ct( v ) - 1 )) : NULL)
381
382#define mdata_vector_remove_last( v ) \
383 (0 < mdata_vector_ct( v ) ? \
384 (mdata_vector_remove( v, mdata_vector_ct( v ) - 1 )) : MERROR_OVERFLOW)
385
386#define mdata_vector_set_ct_step( v, step ) \
387 (v)->ct_step = step;
388
396#define mdata_vector_ct( v ) ((v)->ct)
397
402#define mdata_vector_sz( v ) (((v)->ct_max) * ((v)->item_sz))
403
408#define mdata_vector_fill( v, ct_new, sz ) \
409 retval = mdata_vector_alloc( v, sz, ct_new ); \
410 maug_cleanup_if_not_ok(); \
411 (v)->ct = (ct_new);
412
413#define mdata_vector_is_locked( v ) \
414 (MDATA_VECTOR_FLAG_IS_LOCKED == \
415 (MDATA_VECTOR_FLAG_IS_LOCKED & (v)->flags))
416
417/* TODO: Implement insert sorting. */
418#define mdata_vector_insert_sort( v, i, t, field )
419
420/* TODO: Implement sorting. */
421#define mdata_vector_sort( v, t, field )
422
423#define _mdata_vector_item_ptr( v, idx ) \
424 (&((v)->data_bytes[((idx) * ((v)->item_sz))]))
425
426#define mdata_vector_set_flag( v, flag ) (v)->flags |= (flag)
427
428#define mdata_vector_get_flag( v, flag ) ((flag) == ((v)->flags & (flag)))
429 /* mdata_vector */
431
436
437#define mdata_table_is_locked( t ) \
438 (mdata_vector_is_locked( &((t)->data_cols[0]) ))
439
440#define mdata_table_get( t, key, type ) \
441 ((type*)mdata_table_get_void( t, key ))
442
443#define mdata_table_hash_get( t, hash, sz, type ) \
444 ((type*)mdata_table_hash_get_void( t, hash, sz ))
445
446#define mdata_table_ct( t ) ((t)->data_cols[0].ct)
447
448#define mdata_table_sz( t ) \
449 mdata_vector_sz( &((t)->data_cols[0]) ) + \
450 mdata_vector_sz( &((t)->data_cols[1]) )
451 /* mdata_table */
453
454#define mdata_retval( idx ) (0 > idx ? ((idx) * -1) : MERROR_OK)
455
456#ifdef MDATA_C
457
459 struct MDATA_STRPOOL* sp, mdata_strpool_idx_t idx
460) {
461 MERROR_RETVAL retval = MERROR_OVERFLOW;
462 mdata_strpool_idx_t i = 0;
463 int autolock = 0;
464
465 if( !mdata_strpool_is_locked( sp ) ) {
466 mdata_strpool_lock( sp );
467 autolock = 1;
468 }
469
470 for( i = 0 ; sp->str_sz > i ; i += (size_t)*(&(sp->str_p[i])) ) {
471 if( idx == i ) {
472 retval = MERROR_OK;
473 goto cleanup;
474 }
475 }
476
477cleanup:
478
479 if( autolock ) {
480 mdata_strpool_unlock( sp );
481 }
482
483 return retval;
484}
485
486/* === */
487
488void mdata_strpool_dump( struct MDATA_STRPOOL* sp ) {
489 size_t i = 0;
490 char* strpool_p = NULL;
491
492 maug_mlock( sp->str_h, strpool_p );
493
494 for( i = 0 ; mdata_strpool_sz( sp ) > i ; i++ ) {
495 printf( "0x%02x ", strpool_p[i] );
496 }
497 printf( "\n" );
498
499 if( NULL != strpool_p ) {
500 maug_munlock( sp->str_h, strpool_p );
501 }
502}
503
504/* === */
505
506mdata_strpool_idx_t mdata_strpool_find(
507 struct MDATA_STRPOOL* strpool, const char* str, size_t str_sz
508) {
509 MERROR_RETVAL retval = MERROR_OK;
510 mdata_strpool_idx_t i = 0;
511 char* strpool_p = NULL;
512 size_t* p_str_iter_sz = NULL;
513
514 if( (MAUG_MHANDLE)NULL == strpool->str_h ) {
515 error_printf( "strpool not allocated!" );
516 i = 0;
517 goto cleanup;
518 }
519
520 maug_mlock( strpool->str_h, strpool_p );
521
522 while( i < strpool->str_sz ) {
523 p_str_iter_sz = (size_t*)&(strpool_p[i]);
524 if(
525 0 == strncmp( &(strpool_p[i + sizeof( size_t )]), str, str_sz + 1 )
526 ) {
527 /* String found. Advance past the size before returning. */
528 i += sizeof( size_t );
529#if MDATA_TRACE_LVL > 0
530 debug_printf( MDATA_TRACE_LVL,
531 "found strpool_idx: " SIZE_T_FMT " (" SIZE_T_FMT " bytes): \"%s\"",
532 i, *p_str_iter_sz, &(strpool_p[i]) );
533#endif /* MDATA_TRACE_LVL */
534
535 goto cleanup;
536#if MDATA_TRACE_LVL > 0
537 } else {
538 debug_printf( MDATA_TRACE_LVL,
539 "skipping strpool_idx: " SIZE_T_FMT " (" SIZE_T_FMT
540 " bytes): \"%s\"",
541 i + sizeof( size_t ), *p_str_iter_sz,
542 &(strpool_p[i + sizeof( size_t )]) );
543#endif /* MDATA_TRACE_LVL */
544 }
545 i += *p_str_iter_sz;
546 }
547
548 /* String not found. */
549 i = 0;
550
551cleanup:
552
553 if( MERROR_OK != retval ) {
554 i = 0;
555 }
556
557 if( NULL != strpool_p ) {
558 maug_munlock( strpool->str_h, strpool_p );
559 }
560
561 return i;
562}
563
564/* === */
565
566MAUG_MHANDLE mdata_strpool_extract(
567 struct MDATA_STRPOOL* sp, mdata_strpool_idx_t idx
568) {
569 MERROR_RETVAL retval = MERROR_OK;
570 MAUG_MHANDLE out_h = (MAUG_MHANDLE)NULL;
571 size_t str_sz = 0;
572 char* out_tmp = NULL;
573 int autolock = 0;
574 char* str_src = NULL;
575
576 if( !mdata_strpool_is_locked( sp ) ) {
577 mdata_strpool_lock( sp );
578 autolock = 1;
579 }
580
581 str_src = mdata_strpool_get( sp, idx );
582 if( NULL == str_src ) {
583 error_printf( "invalid strpool index: " SSIZE_T_FMT, idx );
584 retval = MERROR_OVERFLOW;
585 goto cleanup;
586 }
587
588 str_sz = maug_strlen( str_src );
589
590 out_h = maug_malloc( str_sz + 1, 1 );
591 maug_cleanup_if_null_alloc( MAUG_MHANDLE, out_h );
592
593 maug_mlock( out_h, out_tmp );
594 maug_cleanup_if_null_lock( char*, out_tmp );
595
596 maug_mzero( out_tmp, str_sz + 1 );
597
598 /* memcpy is faster, here, since we know the allocated size was succesful.
599 */
600 memcpy( out_tmp, str_src, str_sz );
601
602cleanup:
603
604 if( NULL != out_tmp ) {
605 maug_munlock( out_h, out_tmp );
606 }
607
608 if( MERROR_OK != retval && (MAUG_MHANDLE)NULL != out_h ) {
609 maug_mfree( out_h );
610 }
611
612 if( autolock ) {
613 mdata_strpool_unlock( sp );
614 }
615
616 return out_h;
617}
618
619/* === */
620
621mdata_strpool_idx_t mdata_strpool_append(
622 struct MDATA_STRPOOL* strpool, const char* str, size_t str_sz, uint8_t flags
623) {
624 mdata_strpool_idx_t idx_p_out = 0;
625 char* strpool_p = NULL;
626 MERROR_RETVAL retval = MERROR_OK;
627 size_t* p_str_sz = NULL;
628 size_t alloc_sz = 0;
629
630 if( 0 == str_sz ) {
631 error_printf( "attempted to add zero-length string!" );
632 retval = MERROR_OVERFLOW;
633 goto cleanup;
634 }
635
636 if(
637 0 < strpool->str_sz &&
638 MDATA_STRPOOL_FLAG_DEDUPE == (MDATA_STRPOOL_FLAG_DEDUPE & flags)
639 ) {
640 /* Search the str_stable for an identical string and return that index.
641 */
642 idx_p_out = mdata_strpool_find( strpool, str, str_sz );
643 if( 0 < idx_p_out ) {
644 /* Found, or error returned. */
645#if MDATA_TRACE_LVL > 0
646 debug_printf( MDATA_TRACE_LVL,
647 "found duplicate string for add at index: " SSIZE_T_FMT,
648 idx_p_out );
649#endif /* MDATA_TRACE_LVL */
650 goto cleanup;
651 }
652 }
653
654 /* Pad out allocated space so size_t is always aligned. */
655 alloc_sz = sizeof( size_t ) + str_sz + 1 /* NULL */ +
656 mdata_strpool_padding( str_sz );
657 assert( 0 == alloc_sz % sizeof( size_t ) );
658
659#if MDATA_TRACE_LVL > 0
660 debug_printf( MDATA_TRACE_LVL,
661 "adding size_t (" SIZE_T_FMT " bytes) + string %s (" SIZE_T_FMT
662 " bytes) + 1 NULL + " SIZE_T_FMT " bytes padding to strpool...",
663 sizeof( size_t ), str, str_sz, mdata_strpool_padding( str_sz ) );
664#endif /* MDATA_TRACE_LVL */
665
666 retval = mdata_strpool_alloc( strpool, alloc_sz );
667 maug_cleanup_if_not_ok();
668
669 maug_mlock( strpool->str_h, strpool_p );
670 maug_cleanup_if_null_alloc( char*, strpool_p );
671
672#if MDATA_TRACE_LVL > 0
673 debug_printf( MDATA_TRACE_LVL,
674 "strpool (" SIZE_T_FMT " bytes) locked to: %p",
675 strpool->str_sz, strpool_p );
676#endif /* MDATA_TRACE_LVL */
677
678 /* Add this string at the end of the string pool. */
679 maug_strncpy(
680 &(strpool_p[strpool->str_sz + sizeof( size_t )]), str, str_sz );
681 strpool_p[strpool->str_sz + sizeof( size_t ) + str_sz] = '\0';
682
683 /* Add the size of the string to the strpool. */
684 assert( 0 == strpool->str_sz % sizeof( size_t ) );
685 p_str_sz = (size_t*)&(strpool_p[strpool->str_sz]);
686 *p_str_sz = alloc_sz;
687
688 idx_p_out = strpool->str_sz + sizeof( size_t );
689
690#if MDATA_TRACE_LVL > 0
691 debug_printf( MDATA_TRACE_LVL, "set strpool_idx: " SIZE_T_FMT ": \"%s\"",
692 strpool->str_sz, &(strpool_p[idx_p_out]) );
693#endif /* MDATA_TRACE_LVL */
694
695 /* Set the string pool cursor to the next available spot. */
696 strpool->str_sz += alloc_sz;
697
698cleanup:
699
700 if( MERROR_OK != retval ) {
701 idx_p_out = 0;
702 }
703
704 if( NULL != strpool_p ) {
705 maug_munlock( strpool->str_h, strpool_p );
706 }
707
708 return idx_p_out;
709}
710
711/* === */
712
713MERROR_RETVAL mdata_strpool_alloc(
714 struct MDATA_STRPOOL* strpool, size_t alloc_sz
715) {
716 MERROR_RETVAL retval = MERROR_OK;
717 MAUG_MHANDLE str_h_new = (MAUG_MHANDLE)NULL;
718
719 if( (MAUG_MHANDLE)NULL == strpool->str_h ) {
720#if MDATA_TRACE_LVL > 0
721 debug_printf(
722 MDATA_TRACE_LVL, "creating string pool of " SIZE_T_FMT " chars...",
723 alloc_sz );
724#endif /* MDATA_TRACE_LVL */
725 assert( (MAUG_MHANDLE)NULL == strpool->str_h );
726 strpool->str_h = maug_malloc( alloc_sz, 1 );
727 maug_cleanup_if_null_alloc( MAUG_MHANDLE, strpool->str_h );
728 strpool->str_sz_max = alloc_sz;
729
730 } else if( strpool->str_sz_max <= strpool->str_sz + alloc_sz ) {
731 while( strpool->str_sz_max <= strpool->str_sz + alloc_sz ) {
732#if MDATA_TRACE_LVL > 0
733 debug_printf(
734 MDATA_TRACE_LVL, "enlarging string pool to " SIZE_T_FMT "...",
735 strpool->str_sz_max * 2 );
736#endif /* MDATA_TRACE_LVL */
737 maug_mrealloc_test(
738 str_h_new, strpool->str_h, strpool->str_sz_max, (size_t)2 );
739 strpool->str_sz_max *= 2;
740 }
741 }
742
743cleanup:
744 return retval;
745}
746
747/* === */
748
749void mdata_strpool_free( struct MDATA_STRPOOL* strpool ) {
750 if( (MAUG_MHANDLE)NULL != strpool->str_h ) {
751 maug_mfree( strpool->str_h );
752 }
753}
754
755/* === */
756
757ssize_t mdata_vector_append(
758 struct MDATA_VECTOR* v, const void* item, size_t item_sz
759) {
760 MERROR_RETVAL retval = MERROR_OK;
761 ssize_t idx_out = -1;
762
763 if( 0 < v->item_sz && item_sz != v->item_sz ) {
764 error_printf( "attempting to add item of " SIZE_T_FMT " bytes to vector, "
765 "but vector is already sized for " SIZE_T_FMT "-byte items!",
766 item_sz, v->item_sz );
767 retval = MERROR_OVERFLOW;
768 goto cleanup;
769 }
770
771 mdata_vector_alloc( v, item_sz, v->ct_step );
772
773 /* Lock the vector to work in it a bit. */
774 mdata_vector_lock( v );
775
776 idx_out = v->ct;
777
778 if( NULL != item ) {
779 /* Copy provided item. */
780#if MDATA_TRACE_LVL > 0
781 debug_printf(
782 MDATA_TRACE_LVL, "inserting into vector at index: " SIZE_T_FMT,
783 idx_out );
784#endif /* MDATA_TRACE_LVL */
785
786 memcpy( _mdata_vector_item_ptr( v, idx_out ), item, item_sz );
787 }
788
789 v->ct++;
790
791cleanup:
792
793 if( MERROR_OK != retval ) {
794 error_printf( "error adding to vector: %d", retval );
795 idx_out = retval * -1;
796 assert( 0 > idx_out );
797 }
798
799 mdata_vector_unlock( v );
800
801 return idx_out;
802}
803
804/* === */
805
806ssize_t mdata_vector_insert(
807 struct MDATA_VECTOR* v, const void* item, ssize_t idx, size_t item_sz
808) {
809 MERROR_RETVAL retval = MERROR_OK;
810 ssize_t i = 0;
811
812 assert( 0 <= idx );
813
814 if( 0 < v->item_sz && item_sz != v->item_sz ) {
815 error_printf( "attempting to add item of " SIZE_T_FMT " bytes to vector, "
816 "but vector is already sized for " SIZE_T_FMT "-byte items!",
817 item_sz, v->item_sz );
818 retval = MERROR_OVERFLOW;
819 goto cleanup;
820 }
821
822 if( idx > v->ct ) {
823 error_printf( "attempting to insert beyond end of vector!" );
824 retval = MERROR_OVERFLOW;
825 goto cleanup;
826 }
827
828 mdata_vector_alloc( v, item_sz, v->ct_step );
829
830 /* Lock the vector to work in it a bit. */
831 mdata_vector_lock( v );
832
833 for( i = v->ct ; idx < i ; i-- ) {
834#if MDATA_TRACE_LVL > 0
835 debug_printf( MDATA_TRACE_LVL,
836 "copying vector item " SSIZE_T_FMT " to " SSIZE_T_FMT "...",
837 i - 1, i );
838#endif /* MDATA_TRACE_LVL */
839 memcpy(
840 _mdata_vector_item_ptr( v, i ),
841 _mdata_vector_item_ptr( v, i - 1),
842 item_sz );
843 }
844
845#if MDATA_TRACE_LVL > 0
846 debug_printf(
847 MDATA_TRACE_LVL, "inserting into vector at index: " SIZE_T_FMT, idx );
848#endif /* MDATA_TRACE_LVL */
849 if( NULL != item ) {
850 /* Copy provided item. */
851 memcpy( _mdata_vector_item_ptr( v, idx ), item, item_sz );
852 } else {
853 /* Just blank the given index. */
854 maug_mzero( _mdata_vector_item_ptr( v, idx ), item_sz );
855 }
856
857 v->ct++;
858
859cleanup:
860
861 if( MERROR_OK != retval ) {
862 error_printf( "error adding to vector: %d", retval );
863 idx = retval * -1;
864 assert( 0 > idx );
865 }
866
867 mdata_vector_unlock( v );
868
869 return idx;
870}
871
872/* === */
873
874MERROR_RETVAL mdata_vector_remove( struct MDATA_VECTOR* v, size_t idx ) {
875 MERROR_RETVAL retval = MERROR_OK;
876 size_t i = 0;
877
878 if( mdata_vector_is_locked( v ) ) {
879 error_printf( "vector cannot be resized while locked!" );
880 retval = MERROR_ALLOC;
881 goto cleanup;
882 }
883
884 if( v->ct <= idx ) {
885 error_printf( "index out of range!" );
886 retval = MERROR_OVERFLOW;
887 goto cleanup;
888 }
889
890#if MDATA_TRACE_LVL > 0
891 debug_printf( MDATA_TRACE_LVL, "removing vector item: " SIZE_T_FMT, idx );
892#endif /* MDATA_TRACE_LVL */
893
894 assert( 0 < v->item_sz );
895
896 mdata_vector_lock( v );
897
898 for( i = idx ; v->ct > i + 1 ; i++ ) {
899#if MDATA_TRACE_LVL > 0
900 debug_printf( MDATA_TRACE_LVL,
901 "shifting " SIZE_T_FMT "-byte vector item " SIZE_T_FMT " up by 1...",
902 v->item_sz, i );
903#endif /* MDATA_TRACE_LVL */
904 memcpy(
905 &(v->data_bytes[i * v->item_sz]),
906 &(v->data_bytes[(i + 1) * v->item_sz]),
907 v->item_sz );
908 }
909
910 v->ct--;
911
912cleanup:
913
914 mdata_vector_unlock( v );
915
916 return retval;
917}
918
919/* === */
920
921void* mdata_vector_get_void( const struct MDATA_VECTOR* v, size_t idx ) {
922
923#if MDATA_TRACE_LVL > 0
924 debug_printf( MDATA_TRACE_LVL,
925 "getting vector item " SIZE_T_FMT " (of " SIZE_T_FMT ")...",
926 idx, v->ct );
927#endif /* MDATA_TRACE_LVL */
928
929 assert( 0 == v->ct || NULL != v->data_bytes );
930
931 if( idx >= v->ct ) {
932 return NULL;
933 } else {
934 return _mdata_vector_item_ptr( v, idx );
935 }
936}
937
938/* === */
939
940MERROR_RETVAL mdata_vector_copy(
941 struct MDATA_VECTOR* v_dest, struct MDATA_VECTOR* v_src
942) {
943 MERROR_RETVAL retval = MERROR_OK;
944
945 if( NULL != v_src->data_bytes ) {
946 error_printf( "vector cannot be copied while locked!" );
947 retval = MERROR_ALLOC;
948 goto cleanup;
949 }
950
951 assert( 0 < v_src->item_sz );
952 assert( 0 < v_src->ct_max );
953
954 v_dest->ct_max = v_src->ct_max;
955 v_dest->ct = v_src->ct;
956 v_dest->item_sz = v_src->item_sz;
957#if MDATA_TRACE_LVL > 0
958 debug_printf( MDATA_TRACE_LVL,
959 "copying " SIZE_T_FMT " vector of " SIZE_T_FMT "-byte nodes...",
960 v_src->ct_max, v_src->item_sz );
961#endif /* MDATA_TRACE_LVL */
962 assert( (MAUG_MHANDLE)NULL == v_dest->data_h );
963 v_dest->data_h = maug_malloc( v_src->ct_max, v_src->item_sz );
964 maug_cleanup_if_null_alloc( MAUG_MHANDLE, v_dest->data_h );
965
966 mdata_vector_lock( v_dest );
967 mdata_vector_lock( v_src );
968
969 memcpy( v_dest->data_bytes, v_src->data_bytes,
970 v_src->ct_max * v_src->item_sz );
971
972cleanup:
973
974 mdata_vector_unlock( v_src );
975 mdata_vector_unlock( v_dest );
976
977 return retval;
978}
979
980/* === */
981
983 struct MDATA_VECTOR* v, size_t item_sz, size_t item_ct_init
984) {
985 MERROR_RETVAL retval = MERROR_OK;
986 MAUG_MHANDLE data_h_new = (MAUG_MHANDLE)NULL;
987 size_t new_ct = item_ct_init,
988 new_bytes_start = 0,
989 new_bytes_sz = 0;
990
991 if( NULL != v->data_bytes ) {
992 error_printf( "vector cannot be resized while locked!" );
993 retval = MERROR_ALLOC;
994 goto cleanup;
995 }
996
997 /* Make sure there are free nodes. */
998 if( (MAUG_MHANDLE)NULL == v->data_h ) {
999 assert( 0 == v->ct_max );
1000
1001 if( 0 < item_ct_init ) {
1002#if MDATA_TRACE_LVL > 0
1003 debug_printf( MDATA_TRACE_LVL, "setting step sz: " SIZE_T_FMT,
1004 item_ct_init );
1005#endif /* MDATA_TRACE_LVL */
1006 v->ct_step = item_ct_init;
1007 } else if( 0 == v->ct_step ) {
1008#if MDATA_TRACE_LVL > 0
1009 debug_printf( MDATA_TRACE_LVL, "setting step sz: %d",
1010 MDATA_VECTOR_INIT_STEP_SZ );
1011#endif /* MDATA_TRACE_LVL */
1012 v->ct_step = MDATA_VECTOR_INIT_STEP_SZ;
1013 }
1014 v->ct_max = v->ct_step;
1015#if MDATA_TRACE_LVL > 0
1016 debug_printf( MDATA_TRACE_LVL,
1017 "creating " SIZE_T_FMT " vector of " SIZE_T_FMT "-byte nodes...",
1018 v->ct_max, item_sz );
1019#endif /* MDATA_TRACE_LVL */
1020 assert( (MAUG_MHANDLE)NULL == v->data_h );
1021 v->data_h = maug_malloc( v->ct_max, item_sz );
1022 assert( 0 < item_sz );
1023 v->item_sz = item_sz;
1024 maug_cleanup_if_null_alloc( MAUG_MHANDLE, v->data_h );
1025
1026 /* Zero out the new space. */
1027 mdata_vector_lock( v );
1028 maug_mzero( v->data_bytes, v->ct_max * item_sz );
1029 mdata_vector_unlock( v );
1030
1031 } else if( v->ct_max <= v->ct + 1 || v->ct_max <= item_ct_init ) {
1032 assert( item_sz == v->item_sz );
1033
1034 /* Use ct * 2 or ct_init... whichever is larger! */
1035 if( item_ct_init < v->ct_max + v->ct_step ) {
1036 assert( v->ct_max + v->ct_step > v->ct_max );
1037 new_ct = v->ct_max + v->ct_step;
1038 }
1039
1040 /* Perform the resize. */
1041#if MDATA_TRACE_LVL > 0
1042 debug_printf( MDATA_TRACE_LVL, "enlarging vector to " SIZE_T_FMT "...",
1043 new_ct );
1044#endif /* MDATA_TRACE_LVL */
1045 maug_mrealloc_test( data_h_new, v->data_h, new_ct, item_sz );
1046
1047 /* Zero out the new space. */
1048 new_bytes_start = v->ct_max * v->item_sz;
1049 assert( new_bytes_start >= v->ct_max );
1050 new_bytes_sz = (new_ct * v->item_sz) - new_bytes_start;
1051 assert( new_bytes_sz >= v->item_sz );
1052 mdata_vector_lock( v );
1053 maug_mzero( &(v->data_bytes[new_bytes_start]), new_bytes_sz );
1054 mdata_vector_unlock( v );
1055
1056 v->ct_max = new_ct;
1057 }
1058
1059cleanup:
1060
1061 return retval;
1062}
1063
1064/* === */
1065
1066void mdata_vector_free( struct MDATA_VECTOR* v ) {
1067 if( 0 < v->ct_max ) {
1068 maug_mfree( v->data_h );
1069 }
1070 v->ct = 0;
1071 v->ct_max = 0;
1072 v->item_sz = 0;
1073}
1074
1075/* === */
1076
1077uint32_t mdata_hash( const char* token, size_t token_sz ) {
1078 uint32_t hash_out = 2166136261u; /* Arbitrary fixed prime. */
1079 size_t i = 0;
1080 char c = 0;
1081
1082 for( i = 0 ; token_sz > i ; i++ ) {
1083 c = token[i];
1084
1085 /* Case-insensitive. */
1086 if( 'A' <= c && 'Z' >= c ) {
1087 c += 32;
1088 }
1089
1090 hash_out ^= c;
1091 hash_out *= 16777619u;
1092 }
1093
1094 return hash_out;
1095}
1096
1097/* === */
1098
1099MERROR_RETVAL mdata_table_lock( struct MDATA_TABLE* t ) {
1100 MERROR_RETVAL retval = MERROR_OK;
1101
1102 mdata_vector_lock( &(t->data_cols[0]) );
1103 mdata_vector_lock( &(t->data_cols[1]) );
1104
1105cleanup:
1106
1107 /*
1108 if( MERROR_OK != retval ) {
1109 assert( !mdata_vector_is_locked( &(t->data_cols[0]) ) );
1110 }
1111 */
1112
1113 return retval;
1114}
1115
1116/* === */
1117
1118MERROR_RETVAL mdata_table_unlock( struct MDATA_TABLE* t ) {
1119 MERROR_RETVAL retval = MERROR_OK;
1120
1121 mdata_vector_unlock( &(t->data_cols[0]) );
1122 mdata_vector_unlock( &(t->data_cols[1]) );
1123
1124 if( MERROR_OK != retval ) {
1125 assert( mdata_vector_is_locked( &(t->data_cols[0]) ) );
1126 }
1127
1128 return retval;
1129}
1130
1131/* === */
1132
1133struct MDATA_TABLE_REPLACE_CADDY {
1134 struct MDATA_TABLE_KEY* key;
1135 void* data;
1136};
1137
1138/* === */
1139
1140ssize_t _mdata_table_hunt_index(
1141 const struct MDATA_TABLE* t,
1142 const char* key, uint32_t key_hash, size_t key_sz
1143) {
1144 struct MDATA_TABLE_KEY* key_iter = NULL;
1145 ssize_t i = -1;
1146
1147 if( 0 == mdata_table_ct( t ) ) {
1148 goto cleanup;
1149 }
1150
1151 assert( mdata_vector_is_locked( &(t->data_cols[0]) ) );
1152
1153 /* Hash the key to hunt for if provided. */
1154 if( NULL != key ) {
1155 key_sz = maug_strlen( key );
1156 if( MDATA_TABLE_KEY_SZ_MAX < key_sz ) {
1157 key_sz = MDATA_TABLE_KEY_SZ_MAX;
1158 }
1159 key_hash = mdata_hash( key, key_sz );
1160 }
1161
1162 /* Compare the key to what we have. */
1163 /* TODO: Divide and conquer! */
1164 for( i = 0 ; mdata_vector_ct( &(t->data_cols[0]) ) > i ; i++ ) {
1165 key_iter = mdata_vector_get(
1166 &(t->data_cols[0]), i, struct MDATA_TABLE_KEY );
1167 assert( NULL != key );
1168 if(
1169 key_iter->hash == key_hash &&
1170 key_iter->string_sz == key_sz
1171 ) {
1172#if MDATA_TRACE_LVL > 0
1173 debug_printf( MDATA_TRACE_LVL, "found value for key: %s", key );
1174#endif /* MDATA_TRACE_LVL */
1175 return i;
1176 }
1177 }
1178
1179cleanup:
1180
1181 return -1;
1182}
1183
1184/* === */
1185
1186static MERROR_RETVAL _mdata_table_replace(
1187 const struct MDATA_TABLE_KEY* key, void* data, size_t data_sz,
1188 void* cb_data, size_t cb_data_sz, size_t idx
1189) {
1190 MERROR_RETVAL retval = MERROR_OK;
1191 struct MDATA_TABLE_REPLACE_CADDY* caddy =
1192 (struct MDATA_TABLE_REPLACE_CADDY*)cb_data;
1193
1194 if(
1195 key->hash == caddy->key->hash && key->string_sz == caddy->key->string_sz
1196 ) {
1197#if MDATA_TRACE_LVL > 0
1198 debug_printf( MDATA_TRACE_LVL,
1199 "replacing table data for key %s (%u)...", key->string, key->hash );
1200#endif /* MDATA_TRACE_LVL */
1201 memcpy( data, caddy->data, data_sz );
1202 retval = MERROR_FILE;
1203 }
1204
1205 return retval;
1206}
1207
1208/* === */
1209
1210MERROR_RETVAL mdata_table_iter(
1211 struct MDATA_TABLE* t,
1212 mdata_table_iter_t cb, void* cb_data, size_t cb_data_sz
1213) {
1214 MERROR_RETVAL retval = MERROR_OK;
1215 size_t i = 0;
1216 int autolock = 0;
1217 struct MDATA_TABLE_KEY* p_key = NULL;
1218 char* p_value = NULL;
1219
1220 if( 0 == mdata_table_ct( t ) ) {
1221 return MERROR_OK;
1222 }
1223
1224 if( !mdata_table_is_locked( t ) ) {
1225#if MDATA_TRACE_LVL > 0
1226 debug_printf( MDATA_TRACE_LVL, "engaging table autolock..." );
1227#endif /* MDATA_TRACE_LVL */
1228 mdata_table_lock( t );
1229 autolock = 1;
1230 }
1231
1232 /* Execute the callback for every item. */
1233 for( i = 0 ; mdata_table_ct( t ) > i ; i++ ) {
1234 p_key = mdata_vector_get_void( &(t->data_cols[0]), i );
1235 assert( NULL != p_key );
1236 assert( 0 < p_key->string_sz );
1237 assert( p_key->string_sz == maug_strlen( p_key->string ) );
1238 p_value = mdata_vector_get_void( &(t->data_cols[1]), i );
1239 retval = cb(
1240 p_key, p_value, t->data_cols[1].item_sz, cb_data, cb_data_sz, i );
1241 maug_cleanup_if_not_ok();
1242 }
1243
1244cleanup:
1245
1246 if( autolock ) {
1247 mdata_table_unlock( t );
1248 }
1249
1250 return retval;
1251}
1252
1253/* === */
1254
1255MERROR_RETVAL mdata_table_set(
1256 struct MDATA_TABLE* t, const char* key,
1257 void* value, size_t value_sz
1258) {
1259 MERROR_RETVAL retval = MERROR_OK;
1260 ssize_t idx_key = -1;
1261 ssize_t idx_val = -1;
1262 struct MDATA_TABLE_KEY key_tmp;
1263 struct MDATA_TABLE_REPLACE_CADDY caddy;
1264
1265 assert( 0 < maug_strlen( key ) );
1266
1267 assert( !mdata_table_is_locked( t ) );
1268
1269 assert(
1270 mdata_vector_ct( &(t->data_cols[0]) ) ==
1271 mdata_vector_ct( &(t->data_cols[1]) ) );
1272
1273 /* Get key hash and properties. */
1274 maug_mzero( &key_tmp, sizeof( struct MDATA_TABLE_KEY ) );
1275 maug_strncpy( key_tmp.string, key, MDATA_TABLE_KEY_SZ_MAX );
1276 if( maug_strlen( key ) > MDATA_TABLE_KEY_SZ_MAX + 1 ) {
1277 error_printf(
1278 "key %s is longer than maximum key size! truncating to: %s",
1279 key, key_tmp.string );
1280 }
1281 key_tmp.string_sz = strlen( key_tmp.string );
1282 key_tmp.hash = mdata_hash( key_tmp.string, key_tmp.string_sz );
1283
1284#if MDATA_TRACE_LVL > 0
1285 debug_printf( MDATA_TRACE_LVL,
1286 "attempting to set key %s (%u) to " SIZE_T_FMT "-byte value...",
1287 key_tmp.string, key_tmp.hash, value_sz );
1288#endif /* MDATA_TRACE_LVL */
1289
1290 caddy.key = &key_tmp;
1291 caddy.data = value;
1292
1293 /* Search for the hash. */
1294 /* TODO: Use quicker search. */
1295 retval = mdata_table_iter( t, _mdata_table_replace, &caddy,
1296 sizeof( struct MDATA_TABLE_REPLACE_CADDY ) );
1297 if( MERROR_FILE == retval ) {
1298 /* _mdata_table_replace returned that it replaced an item, so quit. */
1299 retval = MERROR_OK;
1300 goto cleanup;
1301 }
1302
1303 /* TODO: Insert in hash order. */
1304
1305 idx_key = mdata_vector_append(
1306 &(t->data_cols[0]), &key_tmp, sizeof( struct MDATA_TABLE_KEY ) );
1307 assert( 0 <= idx_key );
1308
1309 /* TODO: Atomicity: remove key if value fails! */
1310
1311 idx_val = mdata_vector_append( &(t->data_cols[1]), value, value_sz );
1312 assert( 0 <= idx_val );
1313
1314cleanup:
1315
1316 /* TODO: Set retval! */
1317
1318 return retval;
1319}
1320
1321/* === */
1322
1323MERROR_RETVAL mdata_table_unset(
1324 struct MDATA_TABLE* t, const char* key
1325) {
1326 MERROR_RETVAL retval = MERROR_OK;
1327 int autolock = 0;
1328 ssize_t idx = 0;
1329
1330#if MDATA_TRACE_LVL > 0
1331 debug_printf( MDATA_TRACE_LVL, "unsetting table key: %s", key );
1332#endif /* MDATA_TRACE_LVL */
1333
1334 /* Autolock is fine to have for unset, as there is no returned pointer to
1335 * preserve.
1336 */
1337 if( !mdata_table_is_locked( t ) ) {
1338#if MDATA_TRACE_LVL > 0
1339 debug_printf( MDATA_TRACE_LVL, "autolocking table vectors" );
1340#endif /* MDATA_TRACE_LVL */
1341 assert( !mdata_vector_is_locked( &(t->data_cols[0]) ) );
1342 assert( !mdata_vector_is_locked( &(t->data_cols[1]) ) );
1343 mdata_table_lock( t );
1344 autolock = 1;
1345 }
1346
1347 idx = _mdata_table_hunt_index( t, key, 0, 0 );
1348 if( 0 > idx ) {
1349 goto cleanup;
1350 }
1351
1352 /* Remove the item. */
1353 mdata_table_unlock( t );
1354 mdata_vector_remove( &(t->data_cols[0]), idx );
1355 mdata_vector_remove( &(t->data_cols[1]), idx );
1356
1357cleanup:
1358
1359 if( autolock && mdata_table_is_locked( t ) ) {
1360 mdata_table_unlock( t );
1361 } else if( !autolock && !mdata_table_is_locked( t ) ) {
1362 mdata_table_lock( t );
1363 }
1364
1365 return retval;
1366}
1367
1368/* === */
1369
1370void* mdata_table_get_void( const struct MDATA_TABLE* t, const char* key ) {
1371 MERROR_RETVAL retval = MERROR_OK;
1372 void* value_out = NULL;
1373 ssize_t idx = 0;
1374
1375 assert( mdata_table_is_locked( t ) );
1376
1377#if MDATA_TRACE_LVL > 0
1378 debug_printf( MDATA_TRACE_LVL,
1379 "searching for key: %s (%u)", key, key_hash );
1380#endif /* MDATA_TRACE_LVL */
1381
1382 idx = _mdata_table_hunt_index( t, key, 0, 0 );
1383 if( 0 > idx ) {
1384 retval = MERROR_OVERFLOW;
1385 goto cleanup;
1386 }
1387
1388 value_out = mdata_vector_get_void( &(t->data_cols[1]), idx );
1389
1390cleanup:
1391
1392 if( MERROR_OK != retval ) {
1393 value_out = NULL;
1394 }
1395
1396 return value_out;
1397}
1398
1399/* === */
1400
1401void* mdata_table_hash_get_void(
1402 struct MDATA_TABLE* t, uint32_t key_hash, size_t key_sz
1403) {
1404 MERROR_RETVAL retval = MERROR_OK;
1405 void* value_out = NULL;
1406 ssize_t idx = 0;
1407
1408 assert( mdata_table_is_locked( t ) );
1409
1410#if MDATA_TRACE_LVL > 0
1411 debug_printf( MDATA_TRACE_LVL,
1412 "searching for hash %u (" SIZE_T_FMT ")", key_hash, key_sz );
1413#endif /* MDATA_TRACE_LVL */
1414
1415 idx = _mdata_table_hunt_index( t, NULL, key_hash, key_sz );
1416 if( 0 > idx ) {
1417 retval = MERROR_OVERFLOW;
1418 goto cleanup;
1419 }
1420
1421 value_out = mdata_vector_get_void( &(t->data_cols[1]), idx );
1422
1423cleanup:
1424
1425 if( MERROR_OK != retval ) {
1426 value_out = NULL;
1427 }
1428
1429 return value_out;
1430}
1431
1432/* === */
1433
1434void mdata_table_free( struct MDATA_TABLE* t ) {
1435 mdata_vector_free( &(t->data_cols[0]) );
1436 mdata_vector_free( &(t->data_cols[1]) );
1437 maug_mzero( t, sizeof( struct MDATA_TABLE ) );
1438}
1439
1440#endif /* MDATA_C */
1441 /* maug_data */
1443
1444#endif /* MDATA_H */
1445
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
MAUG_MHANDLE mdata_strpool_extract(struct MDATA_STRPOOL *sp, mdata_strpool_idx_t idx)
Return a dynamically-allocated memory handle containing the contents of the string at the given index...
MERROR_RETVAL mdata_strpool_check_idx(struct MDATA_STRPOOL *sp, mdata_strpool_idx_t idx)
Verify if the given mdata_strpool_idx_t is valid in the given strpool.
ssize_t mdata_vector_append(struct MDATA_VECTOR *v, const void *item, size_t item_sz)
Append an item to the specified vector.
MERROR_RETVAL mdata_vector_remove(struct MDATA_VECTOR *v, size_t idx)
Remove item at the given index, shifting subsequent items up by 1.
void * mdata_vector_get_void(const struct MDATA_VECTOR *v, size_t idx)
Get a generic pointer to an item in the MDATA_VECTOR.
MERROR_RETVAL mdata_vector_alloc(struct MDATA_VECTOR *v, size_t item_sz, size_t item_ct_init)
A pool of immutable text strings. Deduplicates strings to save memory.
Definition mdata.h:68
Definition mdata.h:121
Definition mdata.h:127
A vector of uniformly-sized objects, stored contiguously.
Definition mdata.h:93
size_t ct_step
Number of items added when more space is needed.
Definition mdata.h:104
MAUG_MHANDLE data_h
Handle for allocated items (unlocked).
Definition mdata.h:96
size_t item_sz
Size, in bytes, of each item.
Definition mdata.h:109
size_t ct
Maximum number of items actually used.
Definition mdata.h:102
uint8_t * data_bytes
Handle for allocated items (locked).
Definition mdata.h:98
size_t ct_max
Maximum number of items currently allocated for.
Definition mdata.h:100
ssize_t locks
Lock count, if MDATA_VECTOR_FLAG_REFCOUNT is enabled.
Definition mdata.h:111