1 /*@ Shared implementation of associative map-alike containers.
2 *@ Include once, define a_TYPE correspondingly, include again.
5 * Copyright (c) 2001 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: ISC
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 # define a_ASSOC_MAP_H
26 # undef a_TYPE_IS_DICT
49 # define a_TYPE_CSDICT 1
53 # if a_TYPE == a_TYPE_CSDICT
54 # define a_TYPE a_TYPE_CSDICT
55 # define a_TYPE_IS_DICT 1
56 # define a_TYPE_NAME "su_cs_dict"
57 # define a_T su_cs_dict
58 # define a_T_F(X) su_CONCAT(csd_, X)
59 # define a_T_PRINAME(X) su_CONCAT(su__CS_DICT_, X)
60 # define a_T_PUBNAME(X) su_CONCAT(su_CS_DICT_, X)
61 # define a_T_PRISYM(X) su_CONCAT(su__cs_dict_, X)
62 # define a_T_PUBSYM(X) su_CONCAT(su_cs_dict_, X)
63 # define a_FUN(X) su_CONCAT(a_csdict_, X)
65 # define a_N su__cs_dict_node
66 # define a_N_F(X) su_CONCAT(csdn_, X)
67 # define a_N_ALLOC(SELF,KLEN,FLAGS) \
68 S(struct a_N*,su_ALLOCATE((VSTRUCT_SIZEOF(struct a_N, a_N_F(key)) +\
69 (KLEN) +1), 1, ((FLAGS) & su_STATE_ERR_MASK)))
70 # define a_N_FREE(SELF,NP) su_FREE(NP)
71 # define a_V su_cs_dict_view
72 # define a_V_F(X) su_CONCAT(csdv_, X)
73 # define a_V_PRINAME(X) su_CONCAT(su__CS_DICT_VIEW_, X)
74 # define a_V_PUBNAME(X) su_CONCAT(su_CS_DICT_VIEW_, X)
75 # define a_V_PRISYM(X) su_CONCAT(su__cs_dict_view_, X)
76 # define a_V_PUBSYM(X) su_CONCAT(su_cs_dict_view_, X)
77 # define a_LA a_csdict_lookarg
78 # define a_LA_F(X) su_CONCAT(csdla_, X)
79 # else /* a_TYPE == a_TYPE_CSDICT */
80 # error Unknown a_TYPE
85 struct a_N
*la_last
; /* Only with AUTO_RESORT */
87 u32 la_klen
; /* Only if a_TYPE_IS_DICT */
91 /* force: ignore FROZEN state. Return -1 if we did resize, successfully */
92 SINLINE s32
a_FUN(check_resize
)(struct a_T
*self
, boole force
, u32 xcount
);
93 static s32
a_FUN(resize
)(struct a_T
*self
, u32 xcount
);
96 static s32
a_FUN(assign
)(struct a_T
*self
, struct a_T
const *t
, boole flags
);
99 static s32
a_FUN(node_new
)(struct a_T
*self
, struct a_N
**res
,
100 struct a_LA
*lap
, a_TK
const *key
, void *value
);
103 static s32
a_FUN(replace
)(struct a_T
*self
, struct a_N
*np
, void *value
);
104 static struct a_T
*a_FUN(remove
)(struct a_T
*self
, struct a_N
*np
,
108 a_FUN(check_resize
)(struct a_T
*self
, boole force
, u32 xcount
){
109 /* Simple approach: shift a 64-bit value, do not care for overflow */
116 if(force
|| !(self
->a_T_F(flags
) & a_T_PUBNAME(FROZEN
))){
117 s
= self
->a_T_F(size
);
119 if(xcount
>= (s
<< self
->a_T_F(tshift
)) ||
120 (xcount
< (s
>>= 1) &&
121 (self
->a_T_F(flags
) & a_T_PUBNAME(AUTO_SHRINK
))))
122 rv
= a_FUN(resize
)(self
, xcount
);
124 ASSERT(xcount
== 0 || self
->a_T_F(size
) > 0);
131 a_FUN(resize
)(struct a_T
*self
, u32 xcount
){
133 struct a_N
**narr
, **arr
;
138 size
= self
->a_T_F(size
);
139 pow2
= ((self
->a_T_F(flags
) & a_T_PUBNAME(POW2_SPACED
)) != 0);
141 /* Calculate new size */
146 /* >= to catch initial 0 size */
147 grow
= ((xcount
>> self
->a_T_F(tshift
)) >= size
);
154 ASSERT(nsize
== 0 || nsize
== 1 || IS_POW2(nsize
));
158 else if(UCMP(32, nsize
, <, S32_MIN
))
160 }else if(nsize
> (1u << 1))
163 nsize
= grow
? su_prime_lookup_next(nsize
)
164 : su_prime_lookup_former(nsize
);
166 /* Refuse to excess storage bounds, but do not fail for this: simply
167 * keep on going and place more nodes in the slots.
168 * Because of the numbers we are talking about this is a theoretical
169 * issue (at the time of this writing). */
173 /* (Again, shift 64-bit to avoid overflow) */
174 if((S(u64
,nsize
) << self
->a_T_F(tshift
)) >= xcount
)
176 }else if((nsize
>> 1) <= xcount
)
186 /* Try to allocate new array, give up on failure */
187 narr
= su_TCALLOCF(struct a_N
*, nsize
,
188 (self
->a_T_F(flags
) & su_STATE_ERR_MASK
));
189 if(UNLIKELY(narr
== NIL
)){
194 /* We will succeed: move pointers over */
195 self
->a_T_F(size
) = nsize
;
197 arr
= self
->a_T_F(array
);
198 self
->a_T_F(array
) = narr
;
200 if((xcount
= self
->a_T_F(count
)) > 0){
201 struct a_N
*np
, **npos
, *xnp
;
203 do for(np
= arr
[--size
]; np
!= NIL
; --xcount
){
206 i
= np
->a_N_F(khash
);
207 i
= pow2
? i
& (nsize
- 1) : i
% nsize
;
210 np
= np
->a_N_F(next
);
211 xnp
->a_N_F(next
) = *npos
;
213 }while(xcount
> 0 && size
> 0);
226 a_FUN(assign
)(struct a_T
*self
, struct a_T
const *t
, boole flags
){
231 /* Clear old elements first if there are any, and if necessary */
234 if(self
->a_T_F(count
) > 0){
235 if(self
->a_T_F(flags
) & a_T_PUBNAME(OWNS
))
236 self
= a_T_PUBSYM(clear_elems
)(self
);
237 self
->a_T_F(count
) = 0;
240 ASSERT_NYD_EXEC(t
!= NIL
, rv
= su_ERR_FAULT
);
243 self
->a_T_F(flags
) = t
->a_T_F(flags
);
244 self
->a_T_F(tbox
) = t
->a_T_F(tbox
);
250 if(t
->a_T_F(count
) == 0)
253 /* We actually ignore a resize failure if we do have some backing store to
254 * put elements into! */
255 rv
= a_FUN(check_resize
)(self
, TRU1
, t
->a_T_F(count
));
256 if(rv
> su_ERR_NONE
&& self
->a_T_F(array
) == NIL
)
262 u32 size
, tsize
, tcnt
;
263 struct a_N
**arr
, **tarr
, *np
, *tnp
;
265 arr
= self
->a_T_F(array
);
266 tarr
= t
->a_T_F(array
);
267 size
= self
->a_T_F(size
);
268 tsize
= t
->a_T_F(size
);
269 tcnt
= t
->a_T_F(count
);
270 pow2
= ((self
->a_T_F(flags
) & a_T_PUBNAME(POW2_SPACED
)) != 0);
273 for(tnp
= tarr
[--tsize
]; tnp
!= NIL
; --tcnt
, tnp
= tnp
->a_N_F(next
)){
276 la
.la_khash
= i
= tnp
->a_N_F(khash
);
277 la
.la_slotidx
= S(u32
,i
= pow2
? i
& (size
- 1) : i
% size
);
278 la
.la_slot
= &arr
[i
];
280 la
.la_klen
= tnp
->a_N_F(klen
);
282 rv
= a_FUN(node_new
)(self
, &np
, &la
, tnp
->a_N_F(key
),
284 if(UNLIKELY(rv
!= 0))
290 ASSERT(rv
== su_ERR_NONE
);
297 a_FUN(node_new
)(struct a_T
*self
, struct a_N
**res
, struct a_LA
*lap
,
298 a_TK
const *key
, void *value
){
307 flags
= self
->a_T_F(flags
);
309 if(flags
& a_T_PUBNAME(OWNS
)){
311 xvalue
= value
= (*self
->a_T_F(tbox
)->tb_clone
)(value
,
312 (flags
& su_STATE_ERR_MASK
));
313 if(UNLIKELY(xvalue
== NIL
) && !(flags
& a_T_PUBNAME(NILISVALO
))){
318 ASSERT(flags
& a_T_PUBNAME(NILISVALO
));
321 /* ..so create a new node */
322 np
= a_N_ALLOC(self
, lap
->la_klen
, flags
);
323 if(UNLIKELY(np
== NIL
)){
325 (*self
->a_T_F(tbox
)->tb_delete
)(xvalue
);
326 rv
= su_ERR_NOMEM
; /* (Cannot be overflow error) */
329 ++self
->a_T_F(count
);
331 np
->a_N_F(next
) = *lap
->la_slot
;
333 np
->a_N_F(data
) = value
;
334 np
->a_N_F(khash
) = lap
->la_khash
;
335 np
->a_N_F(klen
) = lap
->la_klen
;
337 # if a_TYPE == a_TYPE_CSDICT
338 su_mem_copy(np
->a_N_F(key
), key
, lap
->la_klen
+1);
339 if(flags
& a_T_PUBNAME(CASE
)){
342 for(nckey
= np
->a_N_F(key
); *nckey
!= '\0'; ++nckey
)
343 *nckey
= su_cs_to_lower(*nckey
);
357 a_FUN(replace
)(struct a_T
*self
, struct a_N
*np
, void *value
){
363 flags
= self
->a_T_F(flags
);
365 if(flags
& a_T_PUBNAME(OWNS
)){
368 ndat
= np
->a_N_F(data
);
370 if(LIKELY(value
!= NIL
)){
371 if((flags
& a_T_PUBNAME(NILISVALO
)) && ndat
!= NIL
){
372 value
= (*self
->a_T_F(tbox
)->tb_assign
)(ndat
,
373 value
, (flags
& su_STATE_ERR_MASK
));
374 if(LIKELY(value
!= NIL
))
379 value
= (*self
->a_T_F(tbox
)->tb_clone
)(value
,
380 (flags
& su_STATE_ERR_MASK
));
381 if(UNLIKELY(value
== NIL
)){
387 ASSERT(flags
& a_T_PUBNAME(NILISVALO
));
390 (*self
->a_T_F(tbox
)->tb_delete
)(ndat
);
393 np
->a_N_F(data
) = value
;
400 a_FUN(remove
)(struct a_T
*self
, struct a_N
*np
, struct a_LA
*lap
){
403 --self
->a_T_F(count
);
405 if(np
->a_N_F(data
) != NIL
&& (self
->a_T_F(flags
) & a_T_PUBNAME(OWNS
)))
406 (*self
->a_T_F(tbox
)->tb_delete
)(np
->a_N_F(data
));
407 if(lap
->la_last
!= NIL
)
408 lap
->la_last
->a_N_F(next
) = np
->a_N_F(next
);
410 *lap
->la_slot
= np
->a_N_F(next
);
418 a_T_PRISYM(lookup
)(struct a_T
const *self
, a_TK
const *key
,
419 void *lookarg_or_nil
){
421 boole
const key_is
= (key
!= NIL
);
427 struct a_N
*rv
, **arr
, *last
;
433 # if a_TYPE == a_TYPE_CSDICT
434 khash
= su_cs_len(key
);
438 khash
= ((self
->a_T_F(flags
) & a_T_PUBNAME(CASE
)) ? su_cs_hash_case_cbuf
439 : su_cs_hash_cbuf
)(key
, klen
);
444 if(LIKELY((slotidx
= self
->a_T_F(size
)) > 0))
445 slotidx
= (self
->a_T_F(flags
) & a_T_PUBNAME(POW2_SPACED
))
446 ? khash
& (slotidx
- 1) : khash
% slotidx
;
447 arr
= self
->a_T_F(array
) + slotidx
;
449 if(lookarg_or_nil
!= NIL
){
452 lap
= S(struct a_LA
*,lookarg_or_nil
);
455 lap
->la_slotidx
= slotidx
;
459 lap
->la_khash
= khash
;
462 if(UNLIKELY((cnt
= self
->a_T_F(count
)) == 0))
465 for(last
= rv
, rv
= *arr
; rv
!= NIL
; last
= rv
, rv
= rv
->a_N_F(next
)){
466 if(khash
!= rv
->a_N_F(khash
))
469 # if a_TYPE == a_TYPE_CSDICT
470 if(klen
!= rv
->a_N_F(klen
))
472 if(((self
->a_T_F(flags
) & a_T_PUBNAME(CASE
))
473 ? R(sz(*)(void const*,void const*,uz
),&su_cs_cmp_case_n
)
474 : &su_mem_cmp
)(key
, rv
->a_N_F(key
), klen
))
482 if(self
->a_T_F(flags
) & a_T_PUBNAME(HEAD_RESORT
)){
483 last
->a_N_F(next
) = rv
->a_N_F(next
);
484 rv
->a_N_F(next
) = *arr
;
486 }else if(lookarg_or_nil
!= NIL
)
487 S(struct a_LA
*,lookarg_or_nil
)->la_last
= last
;
498 a_T_PRISYM(insrep
)(struct a_T
*self
, a_TK
const *key
, void *value
,
499 up replace_and_view_or_nil
){
508 flags
= self
->a_T_F(flags
);
509 viewp
= R(struct a_V
*,replace_and_view_or_nil
& ~TRU1
);
511 /* Ensure this basic precondition */
513 (flags
& a_T_PUBNAME(OWNS
)) && !(flags
& a_T_PUBNAME(NILISVALO
))){
518 /* But on error we will put new node in case we are empty, so create some
519 * array space right away */
520 if(UNLIKELY(self
->a_T_F(size
) == 0) &&
521 (rv
= a_FUN(resize
)(self
, 1)) > su_ERR_NONE
)
524 /* Try to find a yet existing key */
525 np
= a_T_PRISYM(lookup
)(self
, key
, &la
);
527 # if a_TYPE == a_TYPE_CSDICT
528 /* (Ensure documented maximum key length first) */
529 if(UNLIKELY(UCMP(z
, la
.la_klen
, >, S32_MAX
))){
530 rv
= su_state_err(su_STATE_ERR_OVERFLOW
, (flags
& su_STATE_ERR_MASK
),
531 _(a_TYPE_NAME
": insertion: key length excess"));
536 /* Is it an insertion of something new? */
537 if(LIKELY(np
== NIL
)){
538 if(UNLIKELY((rv
= a_FUN(node_new
)(self
, &np
, &la
, key
, value
)
541 /* Never grow array storage if no other node is in this slot.
542 * And do not fail if a resize fails at this point, it would only be
543 * expensive and of no value, especially as it seems the user is
544 * ignoring ENOMEM++ */
545 if(UNLIKELY(np
->a_N_F(next
) != NIL
)){
546 /* If we did resize and we have to know the node location, it seems
547 * easiest and most efficient to simply perform another lookup */
548 if(a_FUN(check_resize
)(self
, FAL0
, self
->a_T_F(count
)
549 ) < su_ERR_NONE
&& viewp
!= NIL
)
550 np
= a_T_PRISYM(lookup
)(self
, key
, &la
);
553 if(LIKELY(replace_and_view_or_nil
& TRU1
) &&
554 ((rv
= a_FUN(replace
)(self
, np
, value
)) != su_ERR_NONE
))
560 if(UNLIKELY(viewp
!= NIL
)){
561 if(LIKELY(rv
<= su_ERR_NONE
)){
562 viewp
->a_V_F(node
) = *la
.la_slot
;
563 viewp
->a_V_F(index
) = la
.la_slotidx
;
565 viewp
->a_V_F(node
) = NIL
;
574 a_T_PRISYM(stats
)(struct a_T
const *self
){
575 ul size
, empties
, multies
, maxper
, i
, j
;
576 struct a_N
**arr
, *np
;
581 su_log_write(su_LOG_INFO
,
582 "----------\n>>> " a_TYPE_NAME
"(%p): statistics\n",
585 arr
= self
->a_T_F(array
);
586 size
= self
->a_T_F(size
);
587 empties
= multies
= maxper
= 0;
589 /* First pass: overall stats */
590 for(i
= 0; i
< size
; ++i
){
592 for(np
= arr
[i
]; np
!= NIL
; np
= np
->a_N_F(next
))
599 maxper
= MAX(maxper
, j
);
602 j
= (multies
!= 0) ? multies
: size
- empties
;
604 j
= self
->a_T_F(count
) / j
;
606 su_log_write(su_LOG_INFO
,
609 # if a_TYPE != a_TYPE_CSDICT
612 # if a_TYPE == a_TYPE_CSDICT
617 " - HEAD_RESORT=%d AUTO_SHRINK=%d FROZEN=%d ERR_PASS=%d NILISVALO=%d\n"
618 " - Array size : %lu\n"
619 " - Elements : %lu\n"
620 " - Tresh-Shift: %u\n"
622 " - Slots: empty: %lu, multiple: %lu, maximum: %lu, avg/multi: ~%lu\n"
623 "* Distribution, visual\n"
625 ((self
->a_T_F(flags
) & a_T_PUBNAME(POW2_SPACED
)) != 0),
626 # if a_TYPE != a_TYPE_CSDICT
627 ((self
->a_T_F(flags
) & a_T_PUBNAME(OWNS_KEYS
)) != 0),
629 # if a_TYPE == a_TYPE_CSDICT
630 ((self
->a_T_F(flags
) & a_T_PUBNAME(CASE
)) != 0),
632 ((self
->a_T_F(flags
) & a_T_PUBNAME(OWNS
)) != 0),
633 ((self
->a_T_F(flags
) & a_T_PUBNAME(HEAD_RESORT
)) != 0),
634 ((self
->a_T_F(flags
) & a_T_PUBNAME(AUTO_SHRINK
)) != 0),
635 ((self
->a_T_F(flags
) & a_T_PUBNAME(FROZEN
)) != 0),
636 ((self
->a_T_F(flags
) & a_T_PUBNAME(ERR_PASS
)) != 0),
637 ((self
->a_T_F(flags
) & a_T_PUBNAME(NILISVALO
)) != 0),
638 (ul
)self
->a_T_F(size
),
639 (ul
)self
->a_T_F(count
),
640 (ui
)self
->a_T_F(tshift
),
641 (ul
)empties
, (ul
)multies
, (ul
)maxper
, (ul
)j
644 /* Second pass: visual distribution */
645 for(i
= 0; i
< size
; ++i
){
649 for(j
= 0, np
= arr
[i
]; np
!= NIL
; np
= np
->a_N_F(next
)){
650 if(++j
>= sizeof(buf
) - 1 -1){
658 su_log_write(su_LOG_INFO
, "%5lu |%s\n", (ul
)i
, buf
);
661 su_log_write(su_LOG_INFO
,
662 "<<<" a_TYPE_NAME
"(%p): statistics\n----------\n",
667 #endif /* DVLOR(1, 0) */
670 a_T_PUBSYM(create
)(struct a_T
*self
, u16 flags
,
671 struct su_toolbox
const *tbox_or_nil
){
675 su_mem_set(self
, 0, sizeof *self
);
677 ASSERT_NYD(!(flags
& a_T_PUBNAME(OWNS
)) ||
678 (tbox_or_nil
!= NIL
&& tbox_or_nil
->tb_clone
!= NIL
&&
679 tbox_or_nil
->tb_delete
!= NIL
&& tbox_or_nil
->tb_assign
!= NIL
));
681 self
->a_T_F(flags
) = (flags
&= a_T_PRINAME(CREATE_MASK
));
682 self
->a_T_F(tshift
) = 2;
683 self
->a_T_F(tbox
) = tbox_or_nil
;
689 a_T_PUBSYM(create_copy
)(struct a_T
*self
, struct a_T
const *t
){
693 su_mem_set(self
, 0, sizeof *self
);
695 ASSERT_NYD(t
!= NIL
);
696 (void)a_FUN(assign
)(self
, t
, TRU1
);
703 a_T_PUBSYM(gut
)(struct a_T
*self
){ /* XXX inline */
707 if(self
->a_T_F(array
) != NIL
)
708 self
= a_T_PUBSYM(clear
)(self
);
713 a_T_PUBSYM(assign
)(struct a_T
*self
, struct a_T
const *t
){ /* XXX inline */
718 rv
= a_FUN(assign
)(self
, t
, TRU1
);
724 a_T_PUBSYM(assign_elems
)(struct a_T
*self
, struct a_T
const *t
){/* XXX inline*/
729 rv
= a_FUN(assign
)(self
, t
, FAL0
);
735 a_T_PUBSYM(clear
)(struct a_T
*self
){
739 if(self
->a_T_F(array
) != NIL
){
740 if(self
->a_T_F(count
) > 0)
741 self
= a_T_PUBSYM(clear_elems
)(self
);
743 su_FREE(self
->a_T_F(array
));
744 self
->a_T_F(array
) = NIL
;
745 self
->a_T_F(size
) = 0;
753 a_T_PUBSYM(clear_elems
)(struct a_T
*self
){
755 struct a_N
**arr
, *np
, *tmp
;
756 su_delete_fun delfun
;
761 cnt
= self
->a_T_F(count
);
762 self
->a_T_F(count
) = 0;
763 size
= self
->a_T_F(size
);
764 delfun
= (self
->a_T_F(flags
) & a_T_PUBNAME(OWNS
))
765 ? self
->a_T_F(tbox
)->tb_delete
: S(su_delete_fun
,NIL
);
766 arr
= self
->a_T_F(array
);
770 if((np
= arr
[--size
]) != NIL
){
775 np
= np
->a_N_F(next
);
778 if(delfun
!= NIL
&& (vp
= tmp
->a_N_F(data
)) != NIL
)
790 a_T_PUBSYM(swap
)(struct a_T
*self
, struct a_T
*t
){
805 a_T_PUBSYM(balance
)(struct a_T
*self
){
809 self
->a_T_F(flags
) &= ~a_T_PUBNAME(FROZEN
);
810 (void)a_FUN(check_resize
)(self
, TRU1
, self
->a_T_F(count
));
817 a_T_PUBSYM(remove
)(struct a_T
*self
, a_TK
const *key
){
822 ASSERT_NYD_EXEC(key
!= NIL
, np
= NIL
);
824 np
= a_T_PRISYM(lookup
)(self
, key
, &la
);
825 if(LIKELY(np
!= NIL
))
826 self
= a_FUN(remove
)(self
, np
, &la
);
833 a_V_PRISYM(move
)(struct a_V
*self
, u8 type
){
835 struct a_N
**arr
, *np
;
840 parent
= self
->a_V_F(parent
);
841 arr
= parent
->a_T_F(array
);
842 size
= parent
->a_T_F(size
);
844 if(type
== a_V_PRINAME(MOVE_BEGIN
)){
845 for(np
= NIL
, idx
= 0; idx
< size
; ++idx
)
846 if((np
= arr
[idx
]) != NIL
)
850 idx
= self
->a_V_F(index
);
851 if(UNLIKELY((np
= self
->a_V_F(node
)->a_N_F(next
)) == NIL
)){
853 if((np
= arr
[idx
]) != NIL
)
857 if(LIKELY(type
!= a_V_PRINAME(MOVE_HAS_NEXT
))){
859 if((self
->a_V_F(node
) = np
) != NIL
){
860 self
->a_V_F(index
) = idx
;
861 self
->a_V_F(next_node
) = NIL
;
864 self
->a_V_F(next_index
) = idx
;
865 self
->a_V_F(next_node
) = np
;
874 a_V_PUBSYM(set_data
)(struct a_V
*self
, void *value
){
879 ASSERT_NYD_EXEC(a_V_PUBSYM(is_valid
)(self
), rv
= su_ERR_INVAL
);
881 parent
= self
->a_V_F(parent
);
886 flags
= parent
->a_T_F(flags
);
888 if((flags
& a_T_PUBNAME(OWNS
)) && !(flags
& a_T_PUBNAME(NILISVALO
))){
894 rv
= a_FUN(replace
)(parent
, self
->a_V_F(node
), value
);
901 a_V_PUBSYM(find
)(struct a_V
*self
, a_TK
const *key
){
908 np
= a_T_PRISYM(lookup
)(self
->a_V_F(parent
), key
, &la
);
910 self
->a_V_F(index
) = la
.la_slotidx
;
911 self
->a_V_F(next_node
) = NIL
;
919 a_V_PUBSYM(remove
)(struct a_V
*self
){
921 struct a_N
*np
, *mynp
;
926 ASSERT_NYD(a_V_PUBSYM(is_valid
)(self
));
928 parent
= self
->a_V_F(parent
);
930 /* Setup the look arg for our position */
931 np
= *(la
.la_slot
= &parent
->a_T_F(array
)[idx
= self
->a_V_F(index
)]);
932 mynp
= self
->a_V_F(node
);
936 while(np
->a_N_F(next
) != mynp
)
937 np
= np
->a_N_F(next
);
941 /* Remove our position */
943 mynp
= mynp
->a_N_F(next
);
944 parent
= a_FUN(remove
)(parent
, np
, &la
);
946 /* Move to the next valid position, if any */
948 self
->a_V_F(node
) = mynp
;
950 while(++idx
< parent
->a_T_F(size
))
951 if((mynp
= parent
->a_T_F(array
)[idx
]) != NIL
)
953 self
->a_V_F(node
) = mynp
;
954 self
->a_V_F(index
) = idx
;
956 self
->a_V_F(next_node
) = NIL
;
962 # undef a_TYPE_CSDICT
964 # undef a_TYPE_IS_DICT
986 #endif /* a_ASSOC_MAP_H */