d7f2202002-10-11Martin Nilsson /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
5b15bb2001-12-10Martin Stjernholm #include "global.h" /* Multisets using rbtree. * * Created by Martin Stjernholm 2001-05-07 */ #include "builtin_functions.h" #include "gc.h" #include "interpret.h" #include "multiset.h"
9e49932004-09-30Martin Nilsson #include "mapping.h"
5b15bb2001-12-10Martin Stjernholm #include "object.h" #include "opcodes.h" #include "pike_error.h" #include "rbtree_low.h" #include "svalue.h"
415b2e2018-03-05Martin Nilsson #include "las.h"
9013c82006-07-07Martin Stjernholm  #ifdef TEST_MULTISET #include "constants.h" #endif
d476592013-06-12Arne Goedeke #include "block_allocator.h"
5b15bb2001-12-10Martin Stjernholm 
0493e62003-11-09Martin Stjernholm /* FIXME: Optimize finds and searches on type fields? (But not when * objects are involved!) Well.. Although cheap I suspect it pays off * so extremely seldom that it isn't worth it. /mast */
5b15bb2001-12-10Martin Stjernholm /* The following defines the allocation policy. It's almost the same * as for mappings. */ #define ALLOC_SIZE(size) ((size) ? (size) + 4 : 0) #define ENLARGE_SIZE(size) (((size) << 1) + 4) #define DO_SHRINK(msd, extra) ((((msd)->size + extra) << 2) + 4 <= (msd)->allocsize)
4fa2a62009-04-05Martin Stjernholm /* For use with TEST_MULTISET: Always goes into the tracking code in * the various find functions, as if a destructed index was encountered. */ /* #define TEST_MULTISET_TRACKING_PATHS */
5b15bb2001-12-10Martin Stjernholm #if defined (PIKE_DEBUG) || defined (TEST_MULTISET) static void debug_dump_ind_data (struct msnode_ind *node, struct multiset_data *msd);
1d116e2018-10-16Henrik Grubbström (Grubba) DECLSPEC(noreturn) static void PIKE_UNUSED_ATTRIBUTE debug_multiset_fatal (
5b15bb2001-12-10Martin Stjernholm  struct multiset *l, const char *fmt, ...) ATTRIBUTE((noreturn, format (printf, 2, 3))); #define multiset_fatal (fprintf (stderr, "%s:%d: Fatal in multiset: ", \ __FILE__, __LINE__), debug_multiset_fatal) #endif #ifdef PIKE_DEBUG /* To get good type checking. */
01b9212016-01-12Per Hedbor static inline union msnode **msnode_ptr_check (union msnode **x)
5b15bb2001-12-10Martin Stjernholm  {return x;}
01b9212016-01-12Per Hedbor static inline struct msnode_ind *msnode_ind_check (struct msnode_ind *x)
5b15bb2001-12-10Martin Stjernholm  {return x;}
b684132014-05-05Per Hedbor static union msnode *low_multiset_find_eq (struct multiset *l, struct svalue *key);
5b15bb2001-12-10Martin Stjernholm #define sub_extra_ref(X) do { \
d631b82002-12-01Martin Stjernholm  if (!sub_ref (X)) Pike_fatal ("Got too few refs to " #X ".\n"); \
5b15bb2001-12-10Martin Stjernholm  } while (0)
b3c3492001-12-10Martin Stjernholm PMOD_EXPORT const char msg_no_multiset_flag_marker[] = "Multiset index lacks MULTISET_FLAG_MARKER. It might be externally clobbered.\n"; PMOD_EXPORT const char msg_multiset_no_node_refs[] = "Multiset got no node refs.\n";
5b15bb2001-12-10Martin Stjernholm #else #define msnode_ptr_check(X) ((union msnode **) (X)) #define msnode_ind_check(X) ((struct msnode_ind *) (X)) #define sub_extra_ref(X) do {sub_ref (X);} while (0) #endif /* #define MULTISET_ALLOC_DEBUG */ #ifdef MULTISET_ALLOC_DEBUG #define ALLOC_TRACE(X) X #else #define ALLOC_TRACE(X) #endif /* #define MULTISET_CMP_DEBUG */ #if defined (MULTISET_CMP_DEBUG) && defined (PIKE_DEBUG) #define INTERNAL_CMP(A, B, CMP_RES) do { \ struct svalue *_cmp_a_ = (A); \ struct svalue *_cmp_b_ = (B); \ int _cmp_res_; \
97ebb32003-01-09Henrik Grubbström (Grubba)  if (Pike_interpreter.trace_level) { \
5b15bb2001-12-10Martin Stjernholm  fputs ("internal cmp ", stderr); \ print_svalue (stderr, _cmp_a_); \
13d0782004-05-19Martin Stjernholm  fprintf (stderr, " (%p) <=> ", _cmp_a_->u.refs); \
5b15bb2001-12-10Martin Stjernholm  print_svalue (stderr, _cmp_b_); \
13d0782004-05-19Martin Stjernholm  fprintf (stderr, " (%p): ", _cmp_b_->u.refs); \
5b15bb2001-12-10Martin Stjernholm  } \ _cmp_res_ = (CMP_RES) = set_svalue_cmpfun (_cmp_a_, _cmp_b_); \
97ebb32003-01-09Henrik Grubbström (Grubba)  if (Pike_interpreter.trace_level) \
5b15bb2001-12-10Martin Stjernholm  fprintf (stderr, "%d\n", _cmp_res_); \ } while (0) #define EXTERNAL_CMP(CMP_LESS) do { \
97ebb32003-01-09Henrik Grubbström (Grubba)  if (Pike_interpreter.trace_level) { \
5b15bb2001-12-10Martin Stjernholm  fputs ("external cmp ", stderr); \
19961b2017-04-08Martin Nilsson  print_svalue (stderr, Pike_sp - 2); \
5b15bb2001-12-10Martin Stjernholm  fputs (" <=> ", stderr); \
19961b2017-04-08Martin Nilsson  print_svalue (stderr, Pike_sp - 1); \
5b15bb2001-12-10Martin Stjernholm  fputs (": ", stderr); \ } \ apply_svalue (CMP_LESS, 2); \
97ebb32003-01-09Henrik Grubbström (Grubba)  if (Pike_interpreter.trace_level) { \
19961b2017-04-08Martin Nilsson  print_svalue (stderr, Pike_sp - 1); \
5b15bb2001-12-10Martin Stjernholm  fputc ('\n', stderr); \ } \ } while (0) #else #define INTERNAL_CMP(A, B, CMP_RES) do { \ (CMP_RES) = set_svalue_cmpfun (A, B); \ } while (0) #define EXTERNAL_CMP(CMP_LESS) do { \ apply_svalue (CMP_LESS, 2); \ } while (0) #endif #define SAME_CMP_LESS(MSD_A, MSD_B) \
7d61ba2008-05-30Martin Stjernholm  is_identical (&(MSD_A)->cmp_less, &(MSD_B)->cmp_less)
5b15bb2001-12-10Martin Stjernholm 
c5e8052008-05-11Martin Stjernholm union nodeptr { union msnode *ms; struct rb_node_hdr *rb; };
5b15bb2001-12-10Martin Stjernholm #define HDR(NODE) ((struct rb_node_hdr *) msnode_check (NODE))
c5e8052008-05-11Martin Stjernholm #define PHDR(NODEPTR) (&((union nodeptr *) msnode_ptr_check (NODEPTR))->rb)
5b15bb2001-12-10Martin Stjernholm #define RBNODE(NODE) ((union msnode *) rb_node_check (NODE)) #define INODE(NODE) ((union msnode *) msnode_ind_check (NODE)) #define NEXT_FREE(NODE) INODE (msnode_check (NODE)->i.next) #define SET_NEXT_FREE(NODE, NEXT) \ (msnode_check (NODE)->i.next = (struct msnode_ind *) msnode_check (NEXT)) #define DELETED_PREV(NODE) INODE (msnode_check (NODE)->i.prev) #define DELETED_NEXT(NODE) ((union msnode *) msnode_check (NODE)->i.ind.u.ptr)
3a488b2014-01-11Arne Goedeke #define NODE_AT(MSD, TYPE, POS) ((struct TYPE *)((char*)(MSD) + OFFSETOF(multiset_data, nodes)) + (POS))
5b15bb2001-12-10Martin Stjernholm #define NODE_OFFSET(TYPE, POS) \
d2361e2003-06-30Martin Stjernholm  PTR_TO_INT (NODE_AT ((struct multiset_data *) NULL, TYPE, POS))
5b15bb2001-12-10Martin Stjernholm  #define SHIFT_PTR(PTR, FROM, TO) ((char *) (PTR) - (char *) (FROM) + (char *) (TO)) #define SHIFT_NODEPTR(NODEPTR, FROM_MSD, TO_MSD) \ ((union msnode *) SHIFT_PTR (msnode_check (NODEPTR), FROM_MSD, TO_MSD)) #define SHIFT_HDRPTR(HDRPTR, FROM_MSD, TO_MSD) \ ((struct rb_node_hdr *) SHIFT_PTR (rb_node_check (HDRPTR), FROM_MSD, TO_MSD)) #define COPY_NODE_PTRS(OLD, OLDBASE, NEW, NEWBASE, TYPE) do { \ (NEW)->prev = (OLD)->prev ? \ (struct TYPE *) SHIFT_PTR ((OLD)->prev, OLDBASE, NEWBASE) : NULL; \ (NEW)->next = (OLD)->next ? \ (struct TYPE *) SHIFT_PTR ((OLD)->next, OLDBASE, NEWBASE) : NULL; \ } while (0) #define COPY_DELETED_PTRS_EXTRA(OLD, OLDBASE, NEW, NEWBASE) do { \ (NEW)->ind.u.ptr = (OLD)->ind.u.ptr ? \ SHIFT_PTR ((OLD)->ind.u.ptr, OLDBASE, NEWBASE) : NULL; \ } while (0) #define COPY_NODE_IND(OLD, NEW, TYPE) do { \ (NEW)->ind = (OLD)->ind; \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE((NEW)->ind, \ TYPEOF((NEW)->ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  add_ref_svalue (&(NEW)->ind); \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE((NEW)->ind, TYPEOF((OLD)->ind)); \
5b15bb2001-12-10Martin Stjernholm  } while (0) #define EXPAND_ARG(X) X #define IGNORE_ARG(X) #define DO_WITH_NODES(MSD) do { \
f84a5a2015-04-26Henrik Grubbström (Grubba)  WITH_NODES_BLOCK (msnode_ind, EXPAND_ARG); \
5b15bb2001-12-10Martin Stjernholm  } while (0) struct multiset *first_multiset = NULL; struct multiset *gc_internal_multiset = NULL; static struct multiset *gc_mark_multiset_pos = NULL; static struct multiset_data empty_ind_msd = {
abe64b2018-05-19Tobias S. Josefowitz  GC_HEADER_INIT(1), 0, NULL, NULL,
8133372008-05-30Martin Stjernholm  SVALUE_INIT_INT (0),
5b15bb2001-12-10Martin Stjernholm  0, 0, 0, BIT_INT, 0,
8133372008-05-30Martin Stjernholm  {{{NULL, NULL, SVALUE_INIT_INT (0)}}}
5b15bb2001-12-10Martin Stjernholm }; void free_multiset_data (struct multiset_data *msd); #define INIT_MULTISET(L) do { \ GC_ALLOC (L); \
50ea682003-03-14Henrik Grubbström (Grubba)  L->refs = 0; \ add_ref(L); /* For DMALLOC... */ \
5b15bb2001-12-10Martin Stjernholm  L->node_refs = 0; \ DOUBLELINK (first_multiset, L); \ } while (0)
d476592013-06-12Arne Goedeke static struct block_allocator multiset_allocator = BA_INIT_PAGES(sizeof(struct multiset), 2);
5b15bb2001-12-10Martin Stjernholm 
d476592013-06-12Arne Goedeke PMOD_EXPORT void really_free_multiset (struct multiset *l) { #ifdef PIKE_DEBUG if (l->refs) { # if DEBUG_MALLOC describe_something (l, T_MULTISET, 0,2,0, NULL); # endif Pike_fatal ("Too few refs %d to multiset.n", l->refs); } if (l->node_refs) { # if DEBUG_MALLOC describe_something (l, T_MULTISET, 0,2,0, NULL); # endif Pike_fatal ("Freeing multiset with %d node refs.n", l->node_refs); } #endif if (!sub_ref (l->msd)) free_multiset_data (l->msd); DOUBLEUNLINK (first_multiset, l); GC_FREE (l); ba_free(&multiset_allocator, l); }
5b15bb2001-12-10Martin Stjernholm 
d476592013-06-12Arne Goedeke PMOD_EXPORT void count_memory_in_multisets (size_t * n, size_t * s) { struct multiset *l; double datasize = 0.0; ba_count_all(&multiset_allocator, n, s); for (l = first_multiset; l; l = l->next)
f84a5a2015-04-26Henrik Grubbström (Grubba)  datasize += (NODE_OFFSET (msnode_ind, l->msd->allocsize)) /
d476592013-06-12Arne Goedeke  (double) l->msd->refs; *s += (size_t) datasize; }
5b15bb2001-12-10Martin Stjernholm 
b684132014-05-05Per Hedbor  static void multiset_delete_node (struct multiset *l, ptrdiff_t nodepos);
5b15bb2001-12-10Martin Stjernholm /* Note: The returned block has no refs. */
cc9f822006-01-14Martin Stjernholm #ifdef PIKE_DEBUG #define low_alloc_multiset_data(ALLOCSIZE, FLAGS) \ low_alloc_multiset_data_2 (ALLOCSIZE, FLAGS, 0) static struct multiset_data *low_alloc_multiset_data_2 (int allocsize, int flags, int allow_alloc_in_gc) #else
7e877a2003-04-02Martin Stjernholm static struct multiset_data *low_alloc_multiset_data (int allocsize, int flags)
cc9f822006-01-14Martin Stjernholm #endif
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd; #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  if (allocsize < 0) Pike_fatal ("Invalid alloc size %d\n", allocsize);
cc9f822006-01-14Martin Stjernholm  if (!allow_alloc_in_gc && Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE) Pike_fatal ("Allocating multiset data blocks within gc is not allowed.\n");
5b15bb2001-12-10Martin Stjernholm #endif
f84a5a2015-04-26Henrik Grubbström (Grubba)  msd = xalloc ( NODE_OFFSET (msnode_ind, allocsize) );
abe64b2018-05-19Tobias S. Josefowitz  gc_init_marker(msd);
5b15bb2001-12-10Martin Stjernholm  msd->refs = msd->noval_refs = 0; msd->root = NULL; msd->allocsize = allocsize; msd->size = 0; msd->ind_types = 0;
f84a5a2015-04-26Henrik Grubbström (Grubba)  msd->val_types = BIT_INT;
5b15bb2001-12-10Martin Stjernholm  msd->flags = flags; msd->free_list = NULL; /* Use fix_free_list to init this. */ ALLOC_TRACE (fprintf (stderr, "%p alloced size %d\n", msd, allocsize)); return msd; } struct tree_build_data { union msnode *list; /* List of nodes in msd linked with rb_next() */ union msnode *node; /* If set, a single extra node in msd. */ struct multiset_data *msd; /* Contains tree finished so far. */ struct multiset *l; /* If set, a multiset with an extra * ref (containing another msd). */ struct multiset_data *msd2; /* If set, yet another msd with an extra ref. */ }; static void free_tree_build_data (struct tree_build_data *build) { union msnode *node, *next;
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  if ((node = build->node)) { \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(node->i.ind, \ TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  free_svalue (&node->i.ind); \ } \ if ((node = build->list)) \ do { \ next = low_multiset_next (node); \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(node->i.ind, \ TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  free_svalue (&node->i.ind); \ } while ((node = next)); DO_WITH_NODES (build->msd); #undef WITH_NODES_BLOCK free_multiset_data (build->msd); if (build->l) free_multiset (build->l); if (build->msd2 && !sub_ref (build->msd2)) free_multiset_data (build->msd2); } void free_multiset_data (struct multiset_data *msd) { union msnode *node, *next; #ifdef PIKE_DEBUG if (msd->refs)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Attempt to free multiset_data with refs.\n");
5b15bb2001-12-10Martin Stjernholm  if (msd->noval_refs)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("There are forgotten noval_refs.\n");
5b15bb2001-12-10Martin Stjernholm #endif /* We trust as few values as possible here, e.g. size and * free_list are ignored. */ GC_FREE_BLOCK (msd); free_svalue (&msd->cmp_less); if ((node = low_multiset_first (msd))) { /* Note: Can't check for MULTISET_FLAG_MARKER here; see e.g. the * error recovery case in mkmultiset_2. */
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  do { \ next = low_multiset_next (node); \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(node->i.ind, \ TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  free_svalue (&node->i.ind); \ } while ((node = next)); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK } ALLOC_TRACE (fprintf (stderr, "%p free\n", msd)); xfree (msd); } static void free_indirect_multiset_data (struct multiset_data **pmsd) { if (*pmsd && !sub_ref (*pmsd)) free_multiset_data (*pmsd); } struct recovery_data { struct multiset_data *a_msd; /* If nonzero, it's freed by free_recovery_data */ struct multiset_data *b_msd; /* If nonzero, it's freed by free_recovery_data */ }; static void free_recovery_data (struct recovery_data *rd) { if (rd->a_msd && !sub_ref (rd->a_msd)) free_multiset_data (rd->a_msd); if (rd->b_msd && !sub_ref (rd->b_msd)) free_multiset_data (rd->b_msd); } /* Links the nodes from and including first_free to the end of the * node block onto (the beginning of) msd->free_list. */ static void fix_free_list (struct multiset_data *msd, int first_free) { int alloclast = msd->allocsize - 1; if (first_free <= alloclast) { union msnode *orig_free_list = msd->free_list;
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  msd->free_list = (union msnode *) NODE_AT (msd, TYPE, first_free); \ for (; first_free < alloclast; first_free++) { \ SET_NEXT_FREE ((union msnode *) NODE_AT (msd, TYPE, first_free), \ (union msnode *) NODE_AT (msd, TYPE, first_free + 1)); \ /* By setting prev to NULL we avoid shifting around garbage in */ \ /* COPY_NODE_PTRS. */ \ NODE_AT (msd, TYPE, first_free)->prev = NULL; \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(NODE_AT (msd, TYPE, first_free)->ind, \ PIKE_T_UNKNOWN); \
5b15bb2001-12-10Martin Stjernholm  } \ SET_NEXT_FREE ((union msnode *) NODE_AT (msd, TYPE, first_free), \ orig_free_list); \ NODE_AT (msd, TYPE, first_free)->prev = NULL; \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(NODE_AT (msd, TYPE, first_free)->ind, \ PIKE_T_UNKNOWN);
5b15bb2001-12-10Martin Stjernholm  DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK } }
b3c3492001-12-10Martin Stjernholm static void clear_deleted_on_free_list (struct multiset_data *msd) { union msnode *node = msd->free_list;
017b572011-10-28Henrik Grubbström (Grubba)  assert (node && TYPEOF(node->i.ind) == T_DELETED);
b3c3492001-12-10Martin Stjernholm  do { node->i.prev = NULL;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(node->i.ind, PIKE_T_UNKNOWN);
b3c3492001-12-10Martin Stjernholm  msd->size--;
017b572011-10-28Henrik Grubbström (Grubba)  } while ((node = NEXT_FREE (node)) && TYPEOF(node->i.ind) == T_DELETED);
b3c3492001-12-10Martin Stjernholm }
5b15bb2001-12-10Martin Stjernholm #define CLEAR_DELETED_ON_FREE_LIST(MSD) do { \
b3c3492001-12-10Martin Stjernholm  assert ((MSD)->refs == 1); \
017b572011-10-28Henrik Grubbström (Grubba)  if ((MSD)->free_list && \ TYPEOF((MSD)->free_list->i.ind) == T_DELETED) \
b3c3492001-12-10Martin Stjernholm  clear_deleted_on_free_list (MSD); \
5b15bb2001-12-10Martin Stjernholm  } while (0) /* The copy has no refs. The copy is verbatim, i.e. the relative node * positions are kept. */ static struct multiset_data *copy_multiset_data (struct multiset_data *old) { /* Note approximate code duplication in resize_multiset_data and * multiset_set_flags. */ int pos = old->allocsize; struct multiset_data *new = low_alloc_multiset_data (pos, old->flags); assign_svalue_no_free (&new->cmp_less, &old->cmp_less); new->ind_types = old->ind_types; new->val_types = old->val_types;
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  struct TYPE *onode, *nnode; \ while (pos-- > 0) { \ onode = NODE_AT (old, TYPE, pos); \ nnode = NODE_AT (new, TYPE, pos); \ COPY_NODE_PTRS (onode, old, nnode, new, TYPE); \
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(onode->ind)) { \
5b15bb2001-12-10Martin Stjernholm  case T_DELETED: \ COPY_DELETED_PTRS_EXTRA (onode, old, nnode, new); \
ff2bc92018-02-15Martin Nilsson  /* FALLTHRU */ \
5b15bb2001-12-10Martin Stjernholm  case PIKE_T_UNKNOWN: \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(nnode->ind, TYPEOF(onode->ind)); \
5b15bb2001-12-10Martin Stjernholm  break; \ default: \ COPY_NODE_IND (onode, nnode, TYPE); \ } \ } DO_WITH_NODES (new); #undef WITH_NODES_BLOCK if (old->free_list) new->free_list = SHIFT_NODEPTR (old->free_list, old, new); if (old->root) new->root = SHIFT_NODEPTR (old->root, old, new); new->size = old->size; ALLOC_TRACE (fprintf (stderr, "%p -> %p: copied, alloc size %d, data size %d\n", old, new, new->allocsize, new->size)); return new; } /* The first part of the new data block is a verbatim copy of the old * one if verbatim is nonzero. This mode also handles link structures * that aren't proper trees. If verbatim is zero, the tree is * rebalanced, since the operation is already linear. The copy has no * refs. * * The resize does not change the refs in referenced svalues, so the * old block is always freed. The refs and noval_refs are transferred * to the new block. */ static struct multiset_data *resize_multiset_data (struct multiset_data *old,
3dd0192014-12-06Martin Nilsson  INT64 newsize, int verbatim)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *new; #ifdef PIKE_DEBUG if (old->refs > 1)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Attempt to resize multiset_data with several refs.\n");
5b15bb2001-12-10Martin Stjernholm  if (verbatim) { if (newsize < old->allocsize)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Cannot shrink multiset_data (from %d to %d) in verbatim mode.\n",
5b15bb2001-12-10Martin Stjernholm  old->allocsize, newsize); } else if (newsize < old->size)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Cannot resize multiset_data with %d elements to %d.\n",
5b15bb2001-12-10Martin Stjernholm  old->size, newsize); if (newsize == old->allocsize)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Unnecessary resize of multiset_data to same size.\n");
5b15bb2001-12-10Martin Stjernholm #endif /* Note approximate code duplication in copy_multiset_data and * multiset_set_flags. */
cc9f822006-01-14Martin Stjernholm #ifdef PIKE_DEBUG
28d6b72006-03-10Martin Stjernholm  /* We can realloc a block even inside the gc. */ new = low_alloc_multiset_data_2 (newsize, old->flags, 1);
cc9f822006-01-14Martin Stjernholm #else
5b15bb2001-12-10Martin Stjernholm  new = low_alloc_multiset_data (newsize, old->flags);
cc9f822006-01-14Martin Stjernholm #endif
941cf22002-11-23Martin Stjernholm  move_svalue (&new->cmp_less, &old->cmp_less);
5b15bb2001-12-10Martin Stjernholm  new->ind_types = old->ind_types; new->val_types = old->val_types; if (verbatim) { int pos = old->allocsize; fix_free_list (new, pos);
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  struct TYPE *oldnodes = (struct TYPE *) old->nodes; \ struct TYPE *newnodes = (struct TYPE *) new->nodes; \ struct TYPE *onode, *nnode; \ while (pos-- > 0) { \ onode = NODE_AT (old, TYPE, pos); \ nnode = NODE_AT (new, TYPE, pos); \ COPY_NODE_PTRS (onode, old, nnode, new, TYPE); \
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(onode->ind)) { \
5b15bb2001-12-10Martin Stjernholm  case T_DELETED: \ COPY_DELETED_PTRS_EXTRA (onode, old, nnode, new); \
ff2bc92018-02-15Martin Nilsson  /* FALLTHRU */ \
5b15bb2001-12-10Martin Stjernholm  case PIKE_T_UNKNOWN: \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(nnode->ind, TYPEOF(onode->ind)); \
5b15bb2001-12-10Martin Stjernholm  break; \ default: \ nnode->ind = onode->ind; \ } \ } DO_WITH_NODES (new); #undef WITH_NODES_BLOCK if (old->free_list) { union msnode *list = SHIFT_NODEPTR (old->free_list, old, new); union msnode *node, *next; for (node = list; (next = NEXT_FREE (node)); node = next) {} SET_NEXT_FREE (node, new->free_list); new->free_list = list; } if (old->root) new->root = SHIFT_NODEPTR (old->root, old, new); new->size = old->size; } else { union msnode *oldnode; if ((oldnode = low_multiset_first (old))) { int pos = 0;
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  struct TYPE *node; \ while (1) { \ node = NODE_AT (new, TYPE, pos); \ node->ind = oldnode->i.ind; \ if (!(oldnode = low_multiset_next (oldnode))) break; \ node->next = NODE_AT (new, TYPE, ++pos); \ } \ NODE_AT (new, TYPE, pos)->next = NULL; DO_WITH_NODES (new); #undef WITH_NODES_BLOCK new->size = ++pos; fix_free_list (new, pos); new->root = RBNODE (rb_make_tree (HDR (new->nodes), pos)); } else fix_free_list (new, 0); } ALLOC_TRACE (fprintf (stderr, "%p -> %p: resized from %d to %d, data size %d (%s)\n", old, new, old->allocsize, new->allocsize, new->size, verbatim ? "verbatim" : "rebalance")); new->refs = old->refs; new->noval_refs = old->noval_refs;
28d6b72006-03-10Martin Stjernholm  GC_REALLOC_BLOCK (old, new);
5b15bb2001-12-10Martin Stjernholm  xfree (old); return new; } #define MOVE_MSD_REF(L, MSD) do { \ sub_extra_ref (MSD); \ add_ref ((MSD) = (L)->msd); \ } while (0) #define MOVE_MSD_REF_AND_FREE(L, MSD) do { \ if (!sub_ref (MSD)) free_multiset_data (MSD); \ add_ref ((MSD) = (L)->msd); \ } while (0) /* There are several occasions when we might get "inflated" msd * blocks, i.e. ones that are larger than the allocation strategy * allows. This happens e.g. when combining node references with * shared data blocks, and when the gc removes nodes in shared data * blocks. Therefore all the copy-on-write functions tries to shrink * them. */
b0d15b2002-09-01Martin Stjernholm static int prepare_for_change (struct multiset *l, int verbatim)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; int msd_changed = 0; #ifdef PIKE_DEBUG if (!verbatim && l->node_refs)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("The verbatim flag not set for multiset with node refs.\n");
5b15bb2001-12-10Martin Stjernholm #endif if (msd->refs > 1) { l->msd = copy_multiset_data (msd); MOVE_MSD_REF (l, msd); msd_changed = 1; if (!l->node_refs) /* Look at l->node_refs and not verbatim here, since when * verbatim is nonzero while l->node_refs is zero, we're only * interested in keeping the tree structure for the allocated * nodes. */ CLEAR_DELETED_ON_FREE_LIST (msd); } if (!verbatim && DO_SHRINK (msd, 0)) { #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0); msd_changed = 1; } return msd_changed; }
b0d15b2002-09-01Martin Stjernholm static int prepare_for_add (struct multiset *l, int verbatim)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; int msd_changed = 0; #ifdef PIKE_DEBUG if (!verbatim && l->node_refs)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("The verbatim flag not set for multiset with node refs.\n");
5b15bb2001-12-10Martin Stjernholm #endif if (msd->refs > 1) { l->msd = copy_multiset_data (msd); MOVE_MSD_REF (l, msd); msd_changed = 1; if (!l->node_refs) CLEAR_DELETED_ON_FREE_LIST (msd); } if (msd->size == msd->allocsize) {
b3c3492001-12-10Martin Stjernholm  if (!l->node_refs) CLEAR_DELETED_ON_FREE_LIST (msd); if (msd->size == msd->allocsize) { /* Can't call check_multiset here, since it might not even be a * proper tree in verbatim mode. */ l->msd = resize_multiset_data (msd, ENLARGE_SIZE (msd->allocsize), verbatim); return 1; }
5b15bb2001-12-10Martin Stjernholm  }
b3c3492001-12-10Martin Stjernholm  if (!verbatim && DO_SHRINK (msd, 1)) {
5b15bb2001-12-10Martin Stjernholm #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size + 1), 0);
b3c3492001-12-10Martin Stjernholm  return 1;
5b15bb2001-12-10Martin Stjernholm  } return msd_changed; } static union msnode *alloc_msnode_verbatim (struct multiset_data *msd) { union msnode *node = msd->free_list; #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  if (!node) Pike_fatal ("Verbatim multiset data block unexpectedly full.\n");
5b15bb2001-12-10Martin Stjernholm #endif
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) == T_DELETED) {
5b15bb2001-12-10Martin Stjernholm  union msnode *prev; do { prev = node; node = NEXT_FREE (node); #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  if (!node) Pike_fatal ("Verbatim multiset data block unexpectedly full.\n");
5b15bb2001-12-10Martin Stjernholm #endif
017b572011-10-28Henrik Grubbström (Grubba)  } while (TYPEOF(node->i.ind) == T_DELETED);
5b15bb2001-12-10Martin Stjernholm  SET_NEXT_FREE (prev, NEXT_FREE (node)); } else msd->free_list = NEXT_FREE (node); msd->size++; return node; } #define ALLOC_MSNODE(MSD, GOT_NODE_REFS, NODE) do { \ if (GOT_NODE_REFS) \ (NODE) = alloc_msnode_verbatim (MSD); \ else { \ (NODE) = (MSD)->free_list; \
8cfc022002-12-22Martin Stjernholm  DO_IF_DEBUG ( \ if (!(NODE)) Pike_fatal ("Multiset data block unexpectedly full.\n"); \ ); \
5b15bb2001-12-10Martin Stjernholm  (MSD)->free_list = NEXT_FREE (NODE); \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF((NODE)->i.ind) == PIKE_T_UNKNOWN) (MSD)->size++; \
5b15bb2001-12-10Martin Stjernholm  } \ } while (0) #define ADD_TO_FREE_LIST(MSD, NODE) do { \ SET_NEXT_FREE (NODE, (MSD)->free_list); \ (MSD)->free_list = (NODE); \ } while (0) static void unlink_msnode (struct multiset *l, struct rbstack_ptr *track, int keep_rbstack) { struct multiset_data *msd = l->msd; struct rbstack_ptr rbstack = *track; union msnode *unlinked_node; if (prepare_for_change (l, 1)) { rbstack_shift (rbstack, HDR (msd->nodes), HDR (l->msd->nodes)); msd = l->msd; } /* Note: Similar code in gc_unlink_node_shared. */ if (l->node_refs) { union msnode *prev, *next; unlinked_node = RBNODE (RBSTACK_PEEK (rbstack)); prev = low_multiset_prev (unlinked_node); next = low_multiset_next (unlinked_node); low_rb_unlink_without_move (PHDR (&msd->root), &rbstack, keep_rbstack); ADD_TO_FREE_LIST (msd, unlinked_node);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(unlinked_node->i.ind, T_DELETED);
5b15bb2001-12-10Martin Stjernholm  unlinked_node->i.prev = (struct msnode_ind *) prev; unlinked_node->i.ind.u.ptr = next; } else { unlinked_node = RBNODE (low_rb_unlink_with_move ( PHDR (&msd->root), &rbstack, keep_rbstack,
f84a5a2015-04-26Henrik Grubbström (Grubba)  sizeof (struct msnode_ind)));
b3c3492001-12-10Martin Stjernholm  CLEAR_DELETED_ON_FREE_LIST (msd);
5b15bb2001-12-10Martin Stjernholm  ADD_TO_FREE_LIST (msd, unlinked_node);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(unlinked_node->i.ind, PIKE_T_UNKNOWN);
5b15bb2001-12-10Martin Stjernholm  unlinked_node->i.prev = NULL; msd->size--; if (!keep_rbstack && DO_SHRINK (msd, 0)) { #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0); } } *track = rbstack; } PMOD_EXPORT void multiset_clear_node_refs (struct multiset *l) { struct multiset_data *msd = l->msd; debug_malloc_touch (l); debug_malloc_touch (msd); assert (!l->node_refs); CLEAR_DELETED_ON_FREE_LIST (msd); if (DO_SHRINK (msd, 0)) { #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0); } } PMOD_EXPORT INT32 multiset_sizeof (struct multiset *l) { INT32 size = l->msd->size; union msnode *node = l->msd->free_list;
017b572011-10-28Henrik Grubbström (Grubba)  for (; node && TYPEOF(node->i.ind) == T_DELETED; node = NEXT_FREE (node))
5b15bb2001-12-10Martin Stjernholm  size--; return size; }
b684132014-05-05Per Hedbor struct multiset *real_allocate_multiset (int allocsize,
cde8152004-09-30Martin Stjernholm  int flags, struct svalue *cmp_less)
5b15bb2001-12-10Martin Stjernholm {
d476592013-06-12Arne Goedeke  struct multiset *l = ba_alloc(&multiset_allocator);
5b15bb2001-12-10Martin Stjernholm 
8cfc022002-12-22Martin Stjernholm  /* FIXME: There's currently little use in making "inflated" * multisets with allocsize, since prepare_for_add shrinks them. */
6ef37a2002-10-27Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm #ifdef PIKE_DEBUG
1ab4ac2008-01-26Martin Stjernholm  if (cmp_less) check_svalue (cmp_less);
5b15bb2001-12-10Martin Stjernholm #endif
7d61ba2008-05-30Martin Stjernholm  if (allocsize ||
017b572011-10-28Henrik Grubbström (Grubba)  (cmp_less && TYPEOF(*cmp_less) != T_INT) ||
f84a5a2015-04-26Henrik Grubbström (Grubba)  (flags)) {
5b15bb2001-12-10Martin Stjernholm  l->msd = low_alloc_multiset_data (allocsize, flags); add_ref (l->msd); fix_free_list (l->msd, 0);
7d61ba2008-05-30Martin Stjernholm  if (cmp_less) { #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*cmp_less) == T_INT) { assert (SUBTYPEOF(*cmp_less) == NUMBER_NUMBER);
7d61ba2008-05-30Martin Stjernholm  assert (cmp_less->u.integer == 0); } #endif assign_svalue_no_free (&l->msd->cmp_less, cmp_less); }
1ab4ac2008-01-26Martin Stjernholm  else {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(l->msd->cmp_less, T_INT, NUMBER_NUMBER, integer, 0);
1ab4ac2008-01-26Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  } else {
f84a5a2015-04-26Henrik Grubbström (Grubba)  l->msd = &empty_ind_msd;
5b15bb2001-12-10Martin Stjernholm  add_ref (l->msd); } INIT_MULTISET (l); return l; } PMOD_EXPORT void do_free_multiset (struct multiset *l) { if (l) { debug_malloc_touch (l); debug_malloc_touch (l->msd); free_multiset (l); } } PMOD_EXPORT void do_sub_msnode_ref (struct multiset *l) { if (l) { debug_malloc_touch (l); debug_malloc_touch (l->msd); sub_msnode_ref (l); } } enum find_types { FIND_EQUAL,
a503492009-04-06Martin Stjernholm  /* FIND_NOEQUAL, */
5b15bb2001-12-10Martin Stjernholm  FIND_LESS, FIND_GREATER, FIND_NOROOT,
a503492009-04-06Martin Stjernholm  FIND_DESTRUCTED, FIND_KEY_DESTRUCTED
5b15bb2001-12-10Martin Stjernholm }; static enum find_types low_multiset_track_eq ( struct multiset_data *msd, struct svalue *key, struct rbstack_ptr *track); static enum find_types low_multiset_track_le_gt ( struct multiset_data *msd, struct svalue *key, struct rbstack_ptr *track);
4fa2a62009-04-05Martin Stjernholm static void midflight_remove_node_fast (struct multiset *l, struct rbstack_ptr *track, int keep_rbstack)
5b15bb2001-12-10Martin Stjernholm { /* If the node index is destructed, we could in principle ignore the * copy-on-write here and remove it in all copies, but then we'd * have to find another way than (l->msd != msd) to signal the tree * change to the calling code. */ struct svalue ind, val; union msnode *node = RBNODE (RBSTACK_PEEK (*track)); /* Postpone free since the msd might be copied in unlink_node. */ low_use_multiset_index (node, ind); unlink_msnode (l, track, keep_rbstack); free_svalue (&ind); } /* Like midflight_remove_node_fast but doesn't bother with concurrent * changes of the multiset or resizing of the msd. There must not be * any node refs to it. */ static void midflight_remove_node_faster (struct multiset_data *msd,
b3c3492001-12-10Martin Stjernholm  struct rbstack_ptr rbstack)
5b15bb2001-12-10Martin Stjernholm { struct svalue ind;
b3c3492001-12-10Martin Stjernholm  union msnode *node = RBNODE (RBSTACK_PEEK (rbstack));
5b15bb2001-12-10Martin Stjernholm  free_svalue (low_use_multiset_index (node, ind)); node = RBNODE (low_rb_unlink_with_move (
b3c3492001-12-10Martin Stjernholm  PHDR (&msd->root), &rbstack, 0,
f84a5a2015-04-26Henrik Grubbström (Grubba)  sizeof (struct msnode_ind)));
b3c3492001-12-10Martin Stjernholm  CLEAR_DELETED_ON_FREE_LIST (msd);
5b15bb2001-12-10Martin Stjernholm  ADD_TO_FREE_LIST (msd, node);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(node->i.ind, PIKE_T_UNKNOWN);
b3c3492001-12-10Martin Stjernholm  msd->size--;
5b15bb2001-12-10Martin Stjernholm } PMOD_EXPORT void multiset_set_flags (struct multiset *l, int flags) { struct multiset_data *old = l->msd; debug_malloc_touch (l); debug_malloc_touch (old);
f84a5a2015-04-26Henrik Grubbström (Grubba)  if (flags != old->flags) { prepare_for_change (l, l->node_refs); l->msd->flags = flags;
5b15bb2001-12-10Martin Stjernholm  } }
b684132014-05-05Per Hedbor static void multiset_set_cmp_less (struct multiset *l,
5b15bb2001-12-10Martin Stjernholm  struct svalue *cmp_less) { struct multiset_data *old = l->msd; #ifdef PIKE_DEBUG debug_malloc_touch (l); debug_malloc_touch (old);
1ab4ac2008-01-26Martin Stjernholm  if (cmp_less) check_svalue (cmp_less);
5b15bb2001-12-10Martin Stjernholm #endif again: if (cmp_less ?
7d61ba2008-05-30Martin Stjernholm  is_identical (cmp_less, &old->cmp_less) :
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(old->cmp_less) == T_INT)
5b15bb2001-12-10Martin Stjernholm  {} else if (!old->root) { if (prepare_for_change (l, l->node_refs)) old = l->msd; free_svalue (&old->cmp_less);
7d61ba2008-05-30Martin Stjernholm  if (cmp_less) { #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*cmp_less) == T_INT) { assert (SUBTYPEOF(*cmp_less) == NUMBER_NUMBER);
7d61ba2008-05-30Martin Stjernholm  assert (cmp_less->u.integer == 0); } #endif assign_svalue_no_free (&old->cmp_less, cmp_less); }
1ab4ac2008-01-26Martin Stjernholm  else {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(old->cmp_less, T_INT, NUMBER_NUMBER, integer, 0);
1ab4ac2008-01-26Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  } else { struct tree_build_data new; union msnode *next; struct svalue ind; ONERROR uwp; SET_ONERROR (uwp, free_tree_build_data, &new); new.l = NULL, new.msd2 = NULL; new.msd = copy_multiset_data (old); new.list = low_multiset_first (new.msd); new.node = NULL; new.msd->root = NULL; free_svalue (&new.msd->cmp_less);
7d61ba2008-05-30Martin Stjernholm  if (cmp_less) { #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*cmp_less) == T_INT) { assert (SUBTYPEOF(*cmp_less) == NUMBER_NUMBER);
7d61ba2008-05-30Martin Stjernholm  assert (cmp_less->u.integer == 0); } #endif assign_svalue_no_free (&new.msd->cmp_less, cmp_less); }
1ab4ac2008-01-26Martin Stjernholm  else {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(new.msd->cmp_less, T_INT, NUMBER_NUMBER, integer, 0);
1ab4ac2008-01-26Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  do { low_use_multiset_index (new.list, ind); next = low_multiset_next (new.list);
13d0782004-05-19Martin Stjernholm  /* Note: Similar code in mkmultiset_2 and copy_multiset_recursively. */
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (&ind)) new.msd->size--; else { while (1) { RBSTACK_INIT (rbstack);
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm  if (!new.msd->root) { low_rb_init_root (HDR (new.msd->root = new.list)); goto node_done; }
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm  switch (low_multiset_track_le_gt (new.msd, &ind, &rbstack)) { case FIND_LESS: low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.list)); goto node_done; case FIND_GREATER: low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.list)); goto node_done; case FIND_DESTRUCTED: midflight_remove_node_faster (new.msd, rbstack); new.msd->size--; break; case FIND_KEY_DESTRUCTED: new.msd->size--; goto node_done; default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n")); }
5b15bb2001-12-10Martin Stjernholm  }
b42e942015-09-28Martin Nilsson  UNREACHABLE();
5b15bb2001-12-10Martin Stjernholm  }
a503492009-04-06Martin Stjernholm  node_done:
5b15bb2001-12-10Martin Stjernholm  if (l->msd != old) { /* l changed. Have to start over to guarantee no loss of data. */ CALL_AND_UNSET_ONERROR (uwp); old = l->msd; goto again; } } while ((new.list = next)); UNSET_ONERROR (uwp); if (!sub_ref (old)) free_multiset_data (old); add_ref (l->msd = new.msd); } }
b684132014-05-05Per Hedbor static struct multiset *mkmultiset_2 (struct array *indices,
f84a5a2015-04-26Henrik Grubbström (Grubba)  struct svalue *cmp_less);
5b15bb2001-12-10Martin Stjernholm  PMOD_EXPORT struct multiset *mkmultiset (struct array *indices) { debug_malloc_touch (indices);
f84a5a2015-04-26Henrik Grubbström (Grubba)  return mkmultiset_2 (indices, NULL);
5b15bb2001-12-10Martin Stjernholm } /* values may be NULL to make a multiset with indices only. */
b684132014-05-05Per Hedbor static struct multiset *mkmultiset_2 (struct array *indices,
f84a5a2015-04-26Henrik Grubbström (Grubba)  struct svalue *cmp_less)
5b15bb2001-12-10Martin Stjernholm { struct multiset *l; struct tree_build_data new; #ifdef PIKE_DEBUG debug_malloc_touch (indices);
1ab4ac2008-01-26Martin Stjernholm  if (cmp_less) check_svalue (cmp_less);
5b15bb2001-12-10Martin Stjernholm #endif new.l = NULL, new.msd2 = NULL;
f84a5a2015-04-26Henrik Grubbström (Grubba)  new.msd = low_alloc_multiset_data (ALLOC_SIZE (indices->size), 0);
5b15bb2001-12-10Martin Stjernholm 
7d61ba2008-05-30Martin Stjernholm  if (cmp_less) { #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(*cmp_less) == T_INT) { assert (SUBTYPEOF(*cmp_less) == NUMBER_NUMBER);
7d61ba2008-05-30Martin Stjernholm  assert (cmp_less->u.integer == 0); } #endif assign_svalue_no_free (&new.msd->cmp_less, cmp_less); }
1ab4ac2008-01-26Martin Stjernholm  else {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(new.msd->cmp_less, T_INT, NUMBER_NUMBER, integer, 0);
1ab4ac2008-01-26Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  if (!indices->size) fix_free_list (new.msd, 0); else {
ce57c82003-11-10Martin Stjernholm  int pos, size = indices->size;
5b15bb2001-12-10Martin Stjernholm  ONERROR uwp; new.list = NULL; SET_ONERROR (uwp, free_tree_build_data, &new); new.msd->ind_types = indices->type_field;
ce57c82003-11-10Martin Stjernholm  for (pos = 0; pos < size; pos++) {
f84a5a2015-04-26Henrik Grubbström (Grubba)  new.node = INODE (NODE_AT (new.msd, msnode_ind, pos));
5b15bb2001-12-10Martin Stjernholm  assign_svalue_no_free (&new.node->i.ind, &ITEM (indices)[pos]);
13d0782004-05-19Martin Stjernholm  /* Note: Similar code in multiset_set_cmp_less and * copy_multiset_recursively. */
5b15bb2001-12-10Martin Stjernholm  /* Note: It would perhaps be a bit faster to use quicksort. */
a503492009-04-06Martin Stjernholm  if (!IS_DESTRUCTED (&new.node->i.ind)) { while (1) { RBSTACK_INIT (rbstack);
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm  if (!new.msd->root) { low_rb_init_root (HDR (new.msd->root = new.node));
5b15bb2001-12-10Martin Stjernholm  goto node_added;
a503492009-04-06Martin Stjernholm  } switch (low_multiset_track_le_gt (new.msd, /* Not clobbered yet. */ &new.node->i.ind, &rbstack)) { case FIND_LESS: low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.node)); goto node_added; case FIND_GREATER: low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.node)); goto node_added; case FIND_DESTRUCTED: midflight_remove_node_faster (new.msd, rbstack); break; case FIND_KEY_DESTRUCTED: goto node_skipped; default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n")); }
5b15bb2001-12-10Martin Stjernholm  }
b42e942015-09-28Martin Nilsson  UNREACHABLE();
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm  node_added:
5b15bb2001-12-10Martin Stjernholm #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(new.node->i.ind, TYPEOF(new.node->i.ind) | MULTISET_FLAG_MARKER);
5b15bb2001-12-10Martin Stjernholm #endif
a503492009-04-06Martin Stjernholm  new.msd->size++; } node_skipped:;
5b15bb2001-12-10Martin Stjernholm  } UNSET_ONERROR (uwp); fix_free_list (new.msd, indices->size); }
d476592013-06-12Arne Goedeke  l = ba_alloc(&multiset_allocator);
5b15bb2001-12-10Martin Stjernholm  l->msd = new.msd; add_ref (new.msd); INIT_MULTISET (l); return l; } PMOD_EXPORT int msnode_is_deleted (struct multiset *l, ptrdiff_t nodepos) { struct multiset_data *msd = l->msd; union msnode *node; struct svalue ind; debug_malloc_touch (l); debug_malloc_touch (msd); check_msnode (l, nodepos, 1); node = OFF2MSNODE (msd, nodepos); if (IS_DESTRUCTED (low_use_multiset_index (node, ind))) { if (msd->refs == 1) {
cc9f822006-01-14Martin Stjernholm  ONERROR uwp;
5b15bb2001-12-10Martin Stjernholm  add_msnode_ref (l);
cc9f822006-01-14Martin Stjernholm  SET_ONERROR (uwp, do_sub_msnode_ref, l);
5b15bb2001-12-10Martin Stjernholm  multiset_delete_node (l, nodepos);
cc9f822006-01-14Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  } return 1; }
017b572011-10-28Henrik Grubbström (Grubba)  return TYPEOF(node->i.ind) == T_DELETED;
5b15bb2001-12-10Martin Stjernholm }
b684132014-05-05Per Hedbor static union msnode *low_multiset_find_eq (struct multiset *l, struct svalue *key)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; struct rb_node_hdr *node; ONERROR uwp; /* Note: Similar code in low_multiset_track_eq. */
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (key)) { /* We only check for a destructed key initially - the result will * be bogus if it's destructed during the search. */ return NULL; }
5b15bb2001-12-10Martin Stjernholm  add_ref (msd); SET_ONERROR (uwp, free_indirect_multiset_data, &msd); while (1) { if ((node = HDR (msd->root))) {
4fa2a62009-04-05Martin Stjernholm #ifdef TEST_MULTISET_TRACKING_PATHS goto index_destructed; #endif
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT) {
5b15bb2001-12-10Martin Stjernholm  struct svalue tmp;
b766502016-05-30Henrik Grubbström (Grubba)  if (!((msd->ind_types | (1 << TYPEOF(*key))) & (BIT_OBJECT | BIT_FUNCTION))) {
5625622011-03-06Martin Stjernholm  /* Can assume an internal order which defines a total order * for all values. */ LOW_RB_FIND ( node, { low_use_multiset_index (RBNODE (node), tmp); assert (!IS_DESTRUCTED (&tmp)); INTERNAL_CMP (key, &tmp, cmp_res); assert (cmp_res != CMPFUN_UNORDERED); }, node = NULL, ;, node = NULL); } else { /* Find the biggest node less or order-wise equal to key. */ LOW_RB_FIND_NEQ ( node, { low_use_multiset_index (RBNODE (node), tmp); if (IS_DESTRUCTED (&tmp)) goto index_destructed; INTERNAL_CMP (key, &tmp, cmp_res); if (!cmp_res) cmp_res = 1; /* Note: CMPFUN_UNORDERED > 0 - treated as equal. */ }, {}, /* Got less or equal or unordered. */ {node = node->prev;}); /* Got greater - step back one. */ /* Step backwards until a less or really equal node is found. */ for (; node; node = rb_prev (node)) { int cmp_res;
5b15bb2001-12-10Martin Stjernholm  low_use_multiset_index (RBNODE (node), tmp); if (IS_DESTRUCTED (&tmp)) goto index_destructed;
5625622011-03-06Martin Stjernholm  if (is_eq (&tmp, key)) break; INTERNAL_CMP (&tmp, key, cmp_res); /* Note: CMPFUN_UNORDERED > 0 - treated as equal. */ if (cmp_res < 0) {node = NULL; break;} } }
5b15bb2001-12-10Martin Stjernholm  } else { /* Find the biggest node less or order-wise equal to key. */ LOW_RB_FIND_NEQ ( node, { push_svalue (key); low_push_multiset_index (RBNODE (node));
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1)) { pop_2_elems(); goto index_destructed; }
5b15bb2001-12-10Martin Stjernholm  EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  cmp_res = UNSAFE_IS_ZERO (Pike_sp - 1) ? 1 : -1;
5b15bb2001-12-10Martin Stjernholm  pop_stack(); }, {}, /* Got less or equal. */ {node = node->prev;}); /* Got greater - step back one. */ /* Step backwards until a less or really equal node is found. */ for (; node; node = rb_prev (node)) { low_push_multiset_index (RBNODE (node));
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1)) { pop_stack(); goto index_destructed; } if (is_eq (Pike_sp - 1, key)) {pop_stack(); break;}
5b15bb2001-12-10Martin Stjernholm  push_svalue (key); EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  if (!UNSAFE_IS_ZERO (Pike_sp - 1)) {pop_stack(); node = NULL; break;}
5b15bb2001-12-10Martin Stjernholm  pop_stack(); } } } if (l->msd == msd) break; /* Will always go into the first if clause below. */ index_destructed: if (l->msd != msd) MOVE_MSD_REF_AND_FREE (l, msd);
4fa2a62009-04-05Martin Stjernholm  else { /* Try again with tracking to be able to remove the destructed node. */ RBSTACK_INIT (rbstack);
cb49bb2014-02-21Per Hedbor  while (msd->root) {
4fa2a62009-04-05Martin Stjernholm  enum find_types find_type = low_multiset_track_eq (msd, key, &rbstack); if (l->msd != msd) MOVE_MSD_REF_AND_FREE (l, msd); else if (find_type == FIND_DESTRUCTED) { sub_extra_ref (msd); midflight_remove_node_fast (l, &rbstack, 0); msd = l->msd; add_ref (msd); /* Let's stay in tracking mode - if there's one destructed * index, there might be many. */ } else { node = find_type == FIND_EQUAL ? RBSTACK_PEEK (rbstack) : NULL; RBSTACK_FREE (rbstack); goto done; } }
cb49bb2014-02-21Per Hedbor  node = NULL; RBSTACK_FREE (rbstack); goto done;
4fa2a62009-04-05Martin Stjernholm  }
cb49bb2014-02-21Per Hedbor  } /* while(1) */
5b15bb2001-12-10Martin Stjernholm 
4fa2a62009-04-05Martin Stjernholm done:
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); sub_extra_ref (msd); return RBNODE (node); } PMOD_EXPORT ptrdiff_t multiset_first (struct multiset *l) { struct multiset_data *msd = l->msd; union msnode *node; struct svalue ind; debug_malloc_touch (l); debug_malloc_touch (msd); node = low_multiset_first (msd); while (node && IS_DESTRUCTED (low_use_multiset_index (node, ind))) if (msd->refs == 1) {
cc9f822006-01-14Martin Stjernholm  ONERROR uwp; add_msnode_ref (l); SET_ONERROR (uwp, do_sub_msnode_ref, l);
5b15bb2001-12-10Martin Stjernholm  multiset_delete_node (l, MSNODE2OFF (msd, node));
cc9f822006-01-14Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  msd = l->msd; node = low_multiset_first (msd); } else node = low_multiset_next (node); if (node) { add_msnode_ref (l); return MSNODE2OFF (msd, node); } else return -1; } PMOD_EXPORT ptrdiff_t multiset_last (struct multiset *l) { struct multiset_data *msd = l->msd; union msnode *node; struct svalue ind; debug_malloc_touch (l); debug_malloc_touch (msd); node = low_multiset_last (msd); while (node && IS_DESTRUCTED (low_use_multiset_index (node, ind))) if (msd->refs == 1) {
cc9f822006-01-14Martin Stjernholm  ONERROR uwp; add_msnode_ref (l); SET_ONERROR (uwp, do_sub_msnode_ref, l);
5b15bb2001-12-10Martin Stjernholm  multiset_delete_node (l, MSNODE2OFF (msd, node));
cc9f822006-01-14Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  msd = l->msd; node = low_multiset_last (msd); } else node = low_multiset_prev (node); if (node) { add_msnode_ref (l); return MSNODE2OFF (msd, node); } else return -1; } /* Returns -1 if there's no predecessor. If the node is deleted, the * predecessor of the closest following nondeleted node is returned. * If there is no following nondeleted node, the last node is
34dbc32003-08-26Martin Stjernholm  * returned. Note that this function never alters the noderefs; it has * no opinion whether you "walk" to the previous node or only "peek" * at it. */
5b15bb2001-12-10Martin Stjernholm PMOD_EXPORT ptrdiff_t multiset_prev (struct multiset *l, ptrdiff_t nodepos) { struct multiset_data *msd = l->msd; union msnode *node; struct svalue ind; debug_malloc_touch (l); debug_malloc_touch (msd); check_msnode (l, nodepos, 1); node = OFF2MSNODE (msd, nodepos);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) == T_DELETED)
5b15bb2001-12-10Martin Stjernholm  do { node = DELETED_NEXT (node); if (!node) { node = low_multiset_last (msd); return node ? MSNODE2OFF (msd, node) : -1; }
017b572011-10-28Henrik Grubbström (Grubba)  } while (TYPEOF(node->i.ind) == T_DELETED);
5b15bb2001-12-10Martin Stjernholm  node = low_multiset_prev (node); while (node && IS_DESTRUCTED (low_use_multiset_index (node, ind))) { union msnode *prev = low_multiset_prev (node); if (msd->refs == 1) {
cc9f822006-01-14Martin Stjernholm  ONERROR uwp;
5b15bb2001-12-10Martin Stjernholm  nodepos = prev ? MSNODE2OFF (msd, prev) : -1; add_msnode_ref (l);
cc9f822006-01-14Martin Stjernholm  SET_ONERROR (uwp, do_sub_msnode_ref, l);
5b15bb2001-12-10Martin Stjernholm  multiset_delete_node (l, MSNODE2OFF (msd, node));
cc9f822006-01-14Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  msd = l->msd; node = nodepos >= 0 ? OFF2MSNODE (msd, nodepos) : NULL; } else node = prev; } return node ? MSNODE2OFF (msd, node) : -1; } /* Returns -1 if there's no successor. If the node is deleted, the * successor of the closest preceding nondeleted node is returned. If
34dbc32003-08-26Martin Stjernholm  * there is no preceding nondeleted node, the first node is returned. * Note that this function never alters the noderefs; it has no * opinion whether you "walk" to the next node or only "peek" at * it. */
5b15bb2001-12-10Martin Stjernholm PMOD_EXPORT ptrdiff_t multiset_next (struct multiset *l, ptrdiff_t nodepos) { struct multiset_data *msd = l->msd; union msnode *node; struct svalue ind; debug_malloc_touch (l); debug_malloc_touch (msd); check_msnode (l, nodepos, 1); node = OFF2MSNODE (msd, nodepos);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) == T_DELETED)
5b15bb2001-12-10Martin Stjernholm  do { node = DELETED_PREV (node); if (!node) { node = low_multiset_first (msd); return node ? MSNODE2OFF (msd, node) : -1; }
017b572011-10-28Henrik Grubbström (Grubba)  } while (TYPEOF(node->i.ind) == T_DELETED);
5b15bb2001-12-10Martin Stjernholm  node = low_multiset_next (node); while (node && IS_DESTRUCTED (low_use_multiset_index (node, ind))) { union msnode *next = low_multiset_next (node); if (msd->refs == 1) {
cc9f822006-01-14Martin Stjernholm  ONERROR uwp;
5b15bb2001-12-10Martin Stjernholm  nodepos = next ? MSNODE2OFF (msd, next) : -1; add_msnode_ref (l);
cc9f822006-01-14Martin Stjernholm  SET_ONERROR (uwp, do_sub_msnode_ref, l);
5b15bb2001-12-10Martin Stjernholm  multiset_delete_node (l, MSNODE2OFF (msd, node));
cc9f822006-01-14Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  msd = l->msd; node = nodepos >= 0 ? OFF2MSNODE (msd, nodepos) : NULL; } else node = next; } return node ? MSNODE2OFF (msd, node) : -1; } static enum find_types low_multiset_track_eq ( struct multiset_data *msd, struct svalue *key, struct rbstack_ptr *track) { struct rb_node_hdr *node = HDR (msd->root); struct rbstack_ptr rbstack = *track;
a503492009-04-06Martin Stjernholm  enum find_types find_type;
2094872009-04-06Martin Stjernholm  ONERROR uwp;
5b15bb2001-12-10Martin Stjernholm  /* Note: Similar code in multiset_find_eq. */ #ifdef PIKE_DEBUG /* Allow zero refs too since that's used during initial building. */
5aad932002-08-15Marcus Comstedt  if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
a503492009-04-06Martin Stjernholm  if (!msd->root) Pike_fatal ("Tree assumed to not be empty here.\n");
5b15bb2001-12-10Martin Stjernholm #endif
2094872009-04-06Martin Stjernholm  SET_ONERROR (uwp, rbstack_do_free, track);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT) {
5b15bb2001-12-10Martin Stjernholm  struct svalue tmp;
5625622011-03-06Martin Stjernholm  if (!(msd->ind_types & (BIT_OBJECT | BIT_FUNCTION))) { /* Can assume an internal order which defines a total order for * all values. */ LOW_RB_TRACK ( rbstack, node, { low_use_multiset_index (RBNODE (node), tmp); assert (!IS_DESTRUCTED (&tmp)); /* TODO: Use special variant of set_svalue_cmpfun so we don't * have to copy the index svalues. */ INTERNAL_CMP (key, &tmp, cmp_res); assert (cmp_res != CMPFUN_UNORDERED); }, {find_type = FIND_LESS; goto done;}, {find_type = FIND_EQUAL; goto done;}, {find_type = FIND_GREATER; goto done;});
b42e942015-09-28Martin Nilsson  UNREACHABLE();
5625622011-03-06Martin Stjernholm  } else { /* Find the biggest node less or order-wise equal to key. */ struct rb_node_hdr *found_node; int step_count; LOW_RB_TRACK_NEQ ( rbstack, node, { low_use_multiset_index (RBNODE (node), tmp); if (IS_DESTRUCTED (&tmp)) { UNSET_ONERROR (uwp); *track = rbstack; return FIND_DESTRUCTED; } INTERNAL_CMP (key, &tmp, cmp_res); if (!cmp_res) cmp_res = 1; /* Note: CMPFUN_UNORDERED > 0 - treated as equal. */ }, { find_type = FIND_LESS; found_node = node; step_count = 0; }, { find_type = FIND_GREATER; found_node = node; node = node->prev; step_count = 1; }); if (IS_DESTRUCTED (key)) { /* An extra check for a destructed key before entering the loop below, * lest we might go backwards through the whole multiset. */ RBSTACK_FREE (rbstack); UNSET_ONERROR (uwp); *track = rbstack; return FIND_KEY_DESTRUCTED; } /* Step backwards until a less or really equal node is found. */ while (1) { int cmp_res; if (!node) goto done;
5b15bb2001-12-10Martin Stjernholm  low_use_multiset_index (RBNODE (node), tmp);
5625622011-03-06Martin Stjernholm  if (IS_DESTRUCTED (&tmp)) {find_type = FIND_DESTRUCTED; break;} if (is_eq (&tmp, key)) {find_type = FIND_EQUAL; break;} INTERNAL_CMP (&tmp, key, cmp_res); /* Note: CMPFUN_UNORDERED > 0 - treated as equal. */ if (cmp_res < 0) goto done; node = rb_prev (node); step_count++; } /* A node was found during stepping. Adjust rbstack. */ while (step_count--) LOW_RB_TRACK_PREV (rbstack, found_node); #ifdef PIKE_DEBUG if (node != RBSTACK_PEEK (rbstack)) Pike_fatal ("Stack stepping failed.\n"); #endif }
5b15bb2001-12-10Martin Stjernholm  } else { /* Find the biggest node less or order-wise equal to key. */ struct rb_node_hdr *found_node; int step_count; LOW_RB_TRACK_NEQ ( rbstack, node, { push_svalue (key); low_push_multiset_index (RBNODE (node));
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1)) {
0f440a2003-04-26Martin Stjernholm  pop_2_elems();
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  *track = rbstack; return FIND_DESTRUCTED; } EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  cmp_res = UNSAFE_IS_ZERO (Pike_sp - 1) ? 1 : -1;
5b15bb2001-12-10Martin Stjernholm  pop_stack(); }, { find_type = FIND_LESS; found_node = node; step_count = 0; }, { find_type = FIND_GREATER; found_node = node; node = node->prev; step_count = 1; });
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (key)) { /* An extra check for a destructed key before entering the loop below, * lest we might go backwards through the whole multiset. */ RBSTACK_FREE (rbstack);
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
a503492009-04-06Martin Stjernholm  *track = rbstack; return FIND_KEY_DESTRUCTED; }
5b15bb2001-12-10Martin Stjernholm  /* Step backwards until a less or really equal node is found. */ while (1) {
a503492009-04-06Martin Stjernholm  if (!node) goto done;
5b15bb2001-12-10Martin Stjernholm  low_push_multiset_index (RBNODE (node));
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1))
a503492009-04-06Martin Stjernholm  {pop_stack(); find_type = FIND_DESTRUCTED; break;}
19961b2017-04-08Martin Nilsson  if (is_eq (Pike_sp - 1, key))
a503492009-04-06Martin Stjernholm  {pop_stack(); find_type = FIND_EQUAL; break;}
5b15bb2001-12-10Martin Stjernholm  push_svalue (key); EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  if (!UNSAFE_IS_ZERO (Pike_sp - 1)) {pop_stack(); goto done;}
5b15bb2001-12-10Martin Stjernholm  pop_stack(); node = rb_prev (node); step_count++; } /* A node was found during stepping. Adjust rbstack. */ while (step_count--) LOW_RB_TRACK_PREV (rbstack, found_node); #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  if (node != RBSTACK_PEEK (rbstack)) Pike_fatal ("Stack stepping failed.\n");
5b15bb2001-12-10Martin Stjernholm #endif
a503492009-04-06Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm 
a503492009-04-06Martin Stjernholm done:
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (key) && find_type != FIND_DESTRUCTED) { RBSTACK_FREE (rbstack);
5b15bb2001-12-10Martin Stjernholm  *track = rbstack;
a503492009-04-06Martin Stjernholm  return FIND_KEY_DESTRUCTED;
5b15bb2001-12-10Martin Stjernholm  }
a503492009-04-06Martin Stjernholm  *track = rbstack; return find_type;
5b15bb2001-12-10Martin Stjernholm } static enum find_types low_multiset_track_le_gt ( struct multiset_data *msd, struct svalue *key, struct rbstack_ptr *track) { struct rb_node_hdr *node = HDR (msd->root); struct rbstack_ptr rbstack = *track;
a503492009-04-06Martin Stjernholm  enum find_types find_type;
2094872009-04-06Martin Stjernholm  ONERROR uwp;
5b15bb2001-12-10Martin Stjernholm  /* Note: Similar code in low_multiset_find_le_gt. */ #ifdef PIKE_DEBUG /* Allow zero refs too since that's used during initial building. */
5aad932002-08-15Marcus Comstedt  if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
a503492009-04-06Martin Stjernholm  if (!msd->root) Pike_fatal ("Tree assumed to not be empty here.\n");
5b15bb2001-12-10Martin Stjernholm #endif
2094872009-04-06Martin Stjernholm  SET_ONERROR (uwp, rbstack_do_free, track);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT) {
5b15bb2001-12-10Martin Stjernholm  struct svalue tmp; LOW_RB_TRACK_NEQ ( rbstack, node, { low_use_multiset_index (RBNODE (node), tmp); if (IS_DESTRUCTED (&tmp)) {
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  *track = rbstack; return FIND_DESTRUCTED; }
b0d15b2002-09-01Martin Stjernholm  /* TODO: Use special variant of set_svalue_cmpfun so we don't
5b15bb2001-12-10Martin Stjernholm  * have to copy the index svalues. */
5625622011-03-06Martin Stjernholm  INTERNAL_CMP (key, low_use_multiset_index (RBNODE (node), tmp), cmp_res); /* Note: CMPFUN_UNORDERED > 0 - treated as equal. */
5b15bb2001-12-10Martin Stjernholm  cmp_res = cmp_res >= 0 ? 1 : -1; },
a503492009-04-06Martin Stjernholm  {find_type = FIND_LESS; goto done;}, {find_type = FIND_GREATER; goto done;});
b42e942015-09-28Martin Nilsson  UNREACHABLE();
5b15bb2001-12-10Martin Stjernholm  } else { LOW_RB_TRACK_NEQ ( rbstack, node, { push_svalue (key); low_push_multiset_index (RBNODE (node));
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1)) {
0f440a2003-04-26Martin Stjernholm  pop_2_elems();
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
5b15bb2001-12-10Martin Stjernholm  *track = rbstack; return FIND_DESTRUCTED; } EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  cmp_res = UNSAFE_IS_ZERO (Pike_sp - 1) ? 1 : -1;
5b15bb2001-12-10Martin Stjernholm  pop_stack(); },
a503492009-04-06Martin Stjernholm  {find_type = FIND_LESS; goto done;}, {find_type = FIND_GREATER; goto done;});
b42e942015-09-28Martin Nilsson  UNREACHABLE();
a503492009-04-06Martin Stjernholm  } done:
2094872009-04-06Martin Stjernholm  UNSET_ONERROR (uwp);
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (key)) { RBSTACK_FREE (rbstack); *track = rbstack; return FIND_KEY_DESTRUCTED;
5b15bb2001-12-10Martin Stjernholm  }
a503492009-04-06Martin Stjernholm  *track = rbstack; return find_type;
5b15bb2001-12-10Martin Stjernholm }
180ad22008-07-22Martin Stjernholm PMOD_EXPORT void multiset_fix_type_field (struct multiset *l)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; union msnode *node; TYPE_FIELD ind_types = 0, val_types = 0; debug_malloc_touch (l); debug_malloc_touch (msd); if ((node = low_multiset_first (msd))) {
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  IND (val_types = BIT_INT); \ do { \
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << (TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  } while ((node = low_multiset_next (node))); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK } else
f84a5a2015-04-26Henrik Grubbström (Grubba)  val_types = BIT_INT;
5b15bb2001-12-10Martin Stjernholm  #ifdef PIKE_DEBUG if (ind_types & ~msd->ind_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset indices type field lacks 0x%x.\n", ind_types & ~msd->ind_types);
5b15bb2001-12-10Martin Stjernholm  if (val_types & ~msd->val_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset values type field lacks 0x%x.\n", val_types & ~msd->val_types);
5b15bb2001-12-10Martin Stjernholm #endif msd->ind_types = ind_types; msd->val_types = val_types; } #ifdef PIKE_DEBUG static void check_multiset_type_fields (struct multiset *l) { struct multiset_data *msd = l->msd; union msnode *node; TYPE_FIELD ind_types = 0, val_types = 0; if ((node = low_multiset_first (msd))) {
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  IND (val_types = BIT_INT); \ do { \
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << (TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK); \
5b15bb2001-12-10Martin Stjernholm  } while ((node = low_multiset_next (node))); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK } else
f84a5a2015-04-26Henrik Grubbström (Grubba)  val_types = BIT_INT;
5b15bb2001-12-10Martin Stjernholm  if (ind_types & ~msd->ind_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset indices type field lacks 0x%x.\n", ind_types & ~msd->ind_types);
b684132014-05-05Per Hedbor  if (val_types & ~msd->val_types) Pike_fatal ("Multiset values type field lacks 0x%x.\n", val_types & ~msd->val_types);
5b15bb2001-12-10Martin Stjernholm }
b684132014-05-05Per Hedbor #endif
5b15bb2001-12-10Martin Stjernholm 
f84a5a2015-04-26Henrik Grubbström (Grubba) #define ADD_NODE(MSD, RBSTACK, NEW, IND, FIND_TYPE) do { \
5b15bb2001-12-10Martin Stjernholm  assign_svalue_no_free (&NEW->i.ind, IND); \
017b572011-10-28Henrik Grubbström (Grubba)  MSD->ind_types |= 1 << TYPEOF(*(IND)); \ DO_IF_DEBUG (SET_SVAL_TYPE(NEW->i.ind, \ TYPEOF(NEW->i.ind) | \ MULTISET_FLAG_MARKER)); \
5b15bb2001-12-10Martin Stjernholm  switch (FIND_TYPE) { \ case FIND_LESS: \ low_rb_link_at_next (PHDR (&MSD->root), RBSTACK, HDR (NEW)); \ break; \ case FIND_GREATER: \ low_rb_link_at_prev (PHDR (&MSD->root), RBSTACK, HDR (NEW)); \ break; \ case FIND_NOROOT: \ MSD->root = NEW; \ low_rb_init_root (HDR (NEW)); \ break; \
b0d15b2002-09-01Martin Stjernholm  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n")); \
5b15bb2001-12-10Martin Stjernholm  } \ } while (0) /* val may be zero. If the multiset has values, the integer 1 will be * used as value in that case. val is ignored if the multiset has no * values. The value of an existing entry will be replaced iff replace * is nonzero (done under the assumption the caller has one value
a503492009-04-06Martin Stjernholm  * lock), otherwise nothing will be done in that case. Nothing is * done if ind is destructed. */
b684132014-05-05Per Hedbor static ptrdiff_t multiset_insert_2 (struct multiset *l,
f84a5a2015-04-26Henrik Grubbström (Grubba)  struct svalue *ind)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; union msnode *new; enum find_types find_type; ONERROR uwp;
90f9602003-01-08Henrik Grubbström (Grubba)  RBSTACK_INIT (rbstack);
5b15bb2001-12-10Martin Stjernholm  /* Note: Similar code in multiset_add, multiset_add_after, * multiset_delete_2 and multiset_delete_node. */ #ifdef PIKE_DEBUG debug_malloc_touch (l); debug_malloc_touch (msd); check_svalue (ind); #endif SET_ONERROR (uwp, free_indirect_multiset_data, &msd); while (1) { if (!msd->root) {
a503492009-04-06Martin Stjernholm  if (IS_DESTRUCTED (ind)) { UNSET_ONERROR (uwp); return -1; }
5b15bb2001-12-10Martin Stjernholm  if (prepare_for_add (l, l->node_refs)) msd = l->msd; ALLOC_MSNODE (msd, l->node_refs, new); find_type = FIND_NOROOT; goto insert; } if (!msd->free_list && !l->node_refs && msd->refs == 1) { /* Enlarge now if possible, anticipating there will be an * insert. Otherwise we either have to redo the search or don't * use a rebalancing resize. */ #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif l->msd = resize_multiset_data (msd, ENLARGE_SIZE (msd->allocsize), 0); msd = l->msd; } add_ref (msd); find_type = low_multiset_track_eq (msd, ind, &rbstack); if (l->msd != msd) { RBSTACK_FREE (rbstack); if (!sub_ref (msd)) free_multiset_data (msd); msd = l->msd; } else switch (find_type) { case FIND_LESS: case FIND_GREATER: sub_extra_ref (msd); if (prepare_for_add (l, 1)) { rbstack_shift (rbstack, HDR (msd->nodes), HDR (l->msd->nodes)); msd = l->msd; } ALLOC_MSNODE (msd, l->node_refs, new); goto insert; case FIND_EQUAL: {
a503492009-04-06Martin Stjernholm  struct rb_node_hdr *node = RBSTACK_PEEK (rbstack);
5b15bb2001-12-10Martin Stjernholm  RBSTACK_FREE (rbstack); UNSET_ONERROR (uwp); sub_extra_ref (msd); return MSNODE2OFF (msd, RBNODE (node)); } case FIND_DESTRUCTED: sub_extra_ref (msd); midflight_remove_node_fast (l, &rbstack, 0); msd = l->msd; break;
a503492009-04-06Martin Stjernholm  case FIND_KEY_DESTRUCTED: sub_extra_ref (msd); UNSET_ONERROR (uwp); return -1;
5aad932002-08-15Marcus Comstedt  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
5b15bb2001-12-10Martin Stjernholm  } } insert: UNSET_ONERROR (uwp);
f84a5a2015-04-26Henrik Grubbström (Grubba)  ADD_NODE (msd, rbstack, new, ind, find_type);
5b15bb2001-12-10Martin Stjernholm  return MSNODE2OFF (msd, new); }
b684132014-05-05Per Hedbor PMOD_EXPORT void multiset_insert (struct multiset *l, struct svalue *ind)
5b15bb2001-12-10Martin Stjernholm { debug_malloc_touch (l);
b684132014-05-05Per Hedbor  debug_malloc_touch (l->msd); dmalloc_touch_svalue (ind);
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_insert_2 (l, ind);
5b15bb2001-12-10Martin Stjernholm } #define TEST_LESS(MSD, A, B, CMP_RES) do { \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(MSD->cmp_less) == T_INT) \
5b15bb2001-12-10Martin Stjernholm  INTERNAL_CMP (A, B, CMP_RES); \ else { \ push_svalue (A); \ push_svalue (B); \ EXTERNAL_CMP (&MSD->cmp_less); \
19961b2017-04-08Martin Nilsson  CMP_RES = UNSAFE_IS_ZERO (Pike_sp - 1) ? 1 : -1; \
5b15bb2001-12-10Martin Stjernholm  pop_stack(); \ } \ } while (0) PMOD_EXPORT int multiset_delete (struct multiset *l, struct svalue *ind) { debug_malloc_touch (l); debug_malloc_touch (l->msd); dmalloc_touch_svalue (ind); return multiset_delete_2 (l, ind, NULL); } /* If removed_val isn't NULL, the value of the deleted node is stored * there, or the integer 1 if the multiset lacks values. The undefined * value is stored if no matching entry was found. */ PMOD_EXPORT int multiset_delete_2 (struct multiset *l, struct svalue *ind, struct svalue *removed_val) { struct multiset_data *msd = l->msd; enum find_types find_type; ONERROR uwp;
90f9602003-01-08Henrik Grubbström (Grubba)  RBSTACK_INIT (rbstack);
5b15bb2001-12-10Martin Stjernholm  /* Note: Similar code in multiset_insert_2, multiset_add, * multiset_add_after and multiset_delete_node. */ debug_malloc_touch (l); debug_malloc_touch (msd); check_svalue (ind);
b0d15b2002-09-01Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  SET_ONERROR (uwp, free_indirect_multiset_data, &msd); while (1) {
a503492009-04-06Martin Stjernholm  if (!msd->root) goto not_found; /* No difference if ind is destructed. */
5b15bb2001-12-10Martin Stjernholm  add_ref (msd); find_type = low_multiset_track_eq (msd, ind, &rbstack); if (l->msd != msd) { RBSTACK_FREE (rbstack); if (!sub_ref (msd)) free_multiset_data (msd); msd = l->msd; } else switch (find_type) { case FIND_LESS: case FIND_GREATER:
a503492009-04-06Martin Stjernholm  case FIND_KEY_DESTRUCTED:
5b15bb2001-12-10Martin Stjernholm  RBSTACK_FREE (rbstack); sub_extra_ref (msd); goto not_found; case FIND_EQUAL: { struct svalue ind, val; struct rb_node_hdr *node = RBSTACK_PEEK (rbstack); UNSET_ONERROR (uwp); sub_extra_ref (msd); /* Postpone free since the msd might be copied in unlink_node. */ low_use_multiset_index (RBNODE (node), ind); unlink_msnode (l, &rbstack, 0); free_svalue (&ind); if (removed_val)
f84a5a2015-04-26Henrik Grubbström (Grubba)  SET_SVAL(*removed_val, T_INT, NUMBER_NUMBER, integer, 1);
5b15bb2001-12-10Martin Stjernholm  return 1; } case FIND_DESTRUCTED: sub_extra_ref (msd); midflight_remove_node_fast (l, &rbstack, 0); msd = l->msd; break;
5aad932002-08-15Marcus Comstedt  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
5b15bb2001-12-10Martin Stjernholm  } } not_found: UNSET_ONERROR (uwp); if (removed_val) {
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(*removed_val, T_INT, NUMBER_UNDEFINED, integer, 0);
5b15bb2001-12-10Martin Stjernholm  } return 0; } /* Frees the node reference that nodepos represents. */
b684132014-05-05Per Hedbor static void multiset_delete_node (struct multiset *l,
5b15bb2001-12-10Martin Stjernholm  ptrdiff_t nodepos) { struct multiset_data *msd = l->msd; enum find_types find_type; ONERROR uwp;
a503492009-04-06Martin Stjernholm  RBSTACK_INIT (rbstack); /* FIXME: Is this always freed properly? */
5b15bb2001-12-10Martin Stjernholm  /* Note: Similar code in multiset_insert_2, multiset_add, * multiset_add_after and multiset_delete_2. */ debug_malloc_touch (l); debug_malloc_touch (msd); check_msnode (l, nodepos, 1); SET_ONERROR (uwp, free_indirect_multiset_data, &msd); while (1) { union msnode *existing = OFF2MSNODE (msd, nodepos); struct svalue ind;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(existing->i.ind) == T_DELETED) {
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); sub_msnode_ref (l); return; } low_use_multiset_index (existing, ind); add_ref (msd); find_type = low_multiset_track_le_gt (msd, &ind, &rbstack); if (l->msd != msd) { RBSTACK_FREE (rbstack); if (!sub_ref (msd)) free_multiset_data (msd); msd = l->msd; } else if (find_type == FIND_DESTRUCTED) { sub_extra_ref (msd); midflight_remove_node_fast (l, &rbstack, 0); msd = l->msd; } else { struct svalue val; struct rb_node_hdr *node = RBSTACK_PEEK (rbstack); /* Step backwards until the existing node is found. */
ef05e22009-04-06Martin Stjernholm  while (RBNODE (node) != existing) { if (!node) { /* The index is destructed, or things changed under our * feet, or perhaps there are flaky compare functions. Build * the stack solely from the rb_node structure instead. */ RBSTACK_FREE (rbstack); low_rb_build_stack (HDR (msd->root), HDR (existing), &rbstack); break; } LOW_RB_TRACK_PREV (rbstack, node); }
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); /* Postpone free since the msd might be copied in unlink_node. */ sub_msnode_ref (l); assert (l->msd == msd); sub_extra_ref (msd); unlink_msnode (l, &rbstack, 0); free_svalue (&ind); return; } } } PMOD_EXPORT int multiset_member (struct multiset *l, struct svalue *key) { debug_malloc_touch (l); debug_malloc_touch (l->msd); dmalloc_touch_svalue (key); return low_multiset_find_eq (l, key) ? 1 : 0; } /* No ref is added for the returned svalue. */ PMOD_EXPORT struct svalue *multiset_lookup (struct multiset *l, struct svalue *key) { union msnode *node; debug_malloc_touch (l); debug_malloc_touch (l->msd); check_svalue (key); if ((node = low_multiset_find_eq (l, key)))
f84a5a2015-04-26Henrik Grubbström (Grubba)  /* Caller better not try to change this. */ return (struct svalue *) &svalue_int_one;
5b15bb2001-12-10Martin Stjernholm  else return NULL; } #define GET_RANGE_SIZE_AND_END(BEGPOS, ENDPOS, MSD, MSD_SIZE, RANGE_SIZE, END) do { \ if (BEGPOS < 0 && ENDPOS < 0) { \ RANGE_SIZE = MSD_SIZE; \ END = low_multiset_last (MSD); \ } \ \ else { \ union msnode *beg, *node; \ \ if (BEGPOS < 0) \ beg = NULL; \ else { \ beg = OFF2MSNODE (MSD, BEGPOS); \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(beg->i.ind) == T_DELETED) { \
5b15bb2001-12-10Martin Stjernholm  do { \ beg = DELETED_PREV (beg); \
017b572011-10-28Henrik Grubbström (Grubba)  } while (beg && TYPEOF(beg->i.ind) == T_DELETED); \
5b15bb2001-12-10Martin Stjernholm  if (beg) beg = low_multiset_next (beg); \ } \ } \ \ if (ENDPOS < 0) { \ END = low_multiset_last (MSD); \ RANGE_SIZE = 1; \ } \ else { \ END = OFF2MSNODE (MSD, ENDPOS); \
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(END->i.ind) == T_DELETED) { \
5b15bb2001-12-10Martin Stjernholm  do { \ END = DELETED_NEXT (END); \
017b572011-10-28Henrik Grubbström (Grubba)  } while (END && TYPEOF(END->i.ind) == T_DELETED); \
5b15bb2001-12-10Martin Stjernholm  if (END) END = low_multiset_prev (END); \ else END = low_multiset_last (MSD); \ } \ RANGE_SIZE = beg ? 1 : 0; \ } \ \ for (node = END; node != beg; node = low_multiset_prev (node)) { \ if (!node) { \ RANGE_SIZE = 0; \ break; \ } \ RANGE_SIZE++; \ } \ } \ } while (0) /* The range is inclusive. begpos and/or endpos may be -1 to go to the * limit in that direction. If begpos points to a deleted node then * the next nondeleted node is used instead, which is found in the * same way as multiset_next. Vice versa for endpos. If the * beginning is after the end then the empty array is returned. */
b684132014-05-05Per Hedbor static struct array *multiset_range_indices (struct multiset *l,
5b15bb2001-12-10Martin Stjernholm  ptrdiff_t begpos, ptrdiff_t endpos) { struct multiset_data *msd; struct array *indices; union msnode *end; int msd_size, range_size; #ifdef PIKE_DEBUG debug_malloc_touch (l); debug_malloc_touch (l->msd); if (begpos >= 0) check_msnode (l, begpos, 1); if (endpos >= 0) check_msnode (l, endpos, 1); #endif check_multiset_for_destruct (l); msd = l->msd; msd_size = multiset_sizeof (l); GET_RANGE_SIZE_AND_END (begpos, endpos, msd, msd_size, range_size, end); if (range_size) { TYPE_FIELD types; indices = allocate_array_no_init (1, range_size); indices->size = range_size; if (range_size == msd_size) { types = msd->ind_types; while (1) { low_assign_multiset_index_no_free (&ITEM (indices)[--range_size], end); if (!range_size) break; end = low_multiset_prev (end); } } else { types = 0; while (1) { low_assign_multiset_index_no_free (&ITEM (indices)[--range_size], end);
017b572011-10-28Henrik Grubbström (Grubba)  types |= 1 << TYPEOF(ITEM (indices)[range_size]);
5b15bb2001-12-10Martin Stjernholm  if (!range_size) break; end = low_multiset_prev (end); } } indices->type_field = types; } else add_ref (indices = &empty_array); return indices; } /* The range is inclusive. begpos and/or endpos may be -1 to go to the * limit in that direction. If begpos points to a deleted node then * the next nondeleted node is used instead, which is found in the * same way as multiset_next. Vice versa for endpos. If the * beginning is after the end then the empty array is returned. */
b684132014-05-05Per Hedbor static struct array *multiset_range_values (struct multiset *l,
5b15bb2001-12-10Martin Stjernholm  ptrdiff_t begpos, ptrdiff_t endpos) { struct multiset_data *msd; struct array *values;
a104562003-05-12Martin Nilsson  union msnode *end;
5b15bb2001-12-10Martin Stjernholm  int msd_size, range_size; #ifdef PIKE_DEBUG debug_malloc_touch (l); debug_malloc_touch (l->msd); if (begpos >= 0) check_msnode (l, begpos, 1); if (endpos >= 0) check_msnode (l, endpos, 1); #endif check_multiset_for_destruct (l); msd = l->msd; msd_size = multiset_sizeof (l); GET_RANGE_SIZE_AND_END (begpos, endpos, msd, msd_size, range_size, end); if (range_size) { values = allocate_array_no_init (1, range_size); values->size = range_size;
f84a5a2015-04-26Henrik Grubbström (Grubba)  while(range_size--) { SET_SVAL(ITEM (values)[range_size], T_INT, NUMBER_NUMBER, integer, 1);
5b15bb2001-12-10Martin Stjernholm  }
f84a5a2015-04-26Henrik Grubbström (Grubba)  values->type_field = BIT_INT;
5b15bb2001-12-10Martin Stjernholm  } else add_ref (values = &empty_array); return values; }
b684132014-05-05Per Hedbor  struct array *multiset_indices (struct multiset *l) { debug_malloc_touch (l); debug_malloc_touch (l->msd); return multiset_range_indices (l, -1, -1); } struct array *multiset_values (struct multiset *l) { debug_malloc_touch (l); debug_malloc_touch (l->msd); return multiset_range_values (l, -1, -1); }
5b15bb2001-12-10Martin Stjernholm /* Eliminates all pointers to destructed objects. If an index is such * a pointer then the node is removed. */ PMOD_EXPORT void check_multiset_for_destruct (struct multiset *l) { struct multiset_data *msd = l->msd; #ifdef PIKE_DEBUG debug_malloc_touch (l); debug_malloc_touch (msd); if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE)
5aad932002-08-15Marcus Comstedt  Pike_fatal("check_multiset_for_destruct called in invalid pass inside gc.\n");
5b15bb2001-12-10Martin Stjernholm #endif if (msd->root && ((msd->ind_types | msd->val_types) & (BIT_OBJECT | BIT_FUNCTION))) { struct rb_node_hdr *node = HDR (msd->root); struct svalue ind; TYPE_FIELD ind_types = 0, val_types = 0; RBSTACK_INIT (rbstack); LOW_RB_TRACK_FIRST (rbstack, node);
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  IND (val_types = BIT_INT); \ do { \ low_use_multiset_index (RBNODE (node), ind); \ if (IS_DESTRUCTED (&ind)) { \ midflight_remove_node_fast (l, &rbstack, 1); \ msd = l->msd; \ node = RBSTACK_PEEK (rbstack); \ } \ else { \
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(ind); \
5b15bb2001-12-10Martin Stjernholm  LOW_RB_TRACK_NEXT (rbstack, node); \ } \ } while (node); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK #ifdef PIKE_DEBUG if (ind_types & ~msd->ind_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset indices type field lacks 0x%x.\n", ind_types & ~msd->ind_types);
5b15bb2001-12-10Martin Stjernholm  if (val_types & ~msd->val_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset values type field lacks 0x%x.\n", val_types & ~msd->val_types);
5b15bb2001-12-10Martin Stjernholm #endif msd->ind_types = ind_types; msd->val_types = val_types; } } PMOD_EXPORT struct multiset *copy_multiset (struct multiset *l) { struct multiset_data *msd = l->msd; debug_malloc_touch (l); debug_malloc_touch (msd);
d476592013-06-12Arne Goedeke  l = ba_alloc(&multiset_allocator);
5b15bb2001-12-10Martin Stjernholm  INIT_MULTISET (l); add_ref (l->msd = msd); return l; } /* Returns NULL if no special case is applicable. */ static struct multiset *merge_special (struct multiset *a, struct multiset *b, int operation) { ONERROR uwp; int op; debug_malloc_touch (a); debug_malloc_touch (a->msd); debug_malloc_touch (b); debug_malloc_touch (b->msd); #define COPY_MSD_AND_KEEP_FLAGS(FROM, TO) do { \ struct multiset_data *oldmsd = (TO)->msd; \ SET_ONERROR (uwp, free_indirect_multiset_data, &oldmsd); \ add_ref ((TO)->msd = (FROM)->msd); \ multiset_set_flags ((TO), oldmsd->flags); \
7d61ba2008-05-30Martin Stjernholm  multiset_set_cmp_less ((TO), &oldmsd->cmp_less); \
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); \ if (!sub_ref (oldmsd)) free_multiset_data (oldmsd); \ } while (0) #define EMPTY_MSD_AND_KEEP_FLAGS(L) do { \ struct multiset_data *oldmsd = (L)->msd; \ add_ref ((L)->msd = low_alloc_multiset_data (0, oldmsd->flags)); \ assign_svalue_no_free (&(L)->msd->cmp_less, &oldmsd->cmp_less); \ if (!sub_ref (oldmsd)) free_multiset_data (oldmsd); \ } while (0) #define ALLOC_COPY_AND_SET_FLAGS(FROM, RES, FLAGSRC) do { \ (RES) = copy_multiset (FROM); \ SET_ONERROR (uwp, do_free_multiset, (RES)); \ multiset_set_flags ((RES), (FLAGSRC)->msd->flags); \
7d61ba2008-05-30Martin Stjernholm  multiset_set_cmp_less ((RES), &(FLAGSRC)->msd->cmp_less); \
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); \ } while (0) #define ALLOC_EMPTY_AND_SET_FLAGS(RES, FLAGSRC) do { \ (RES) = allocate_multiset (0, (FLAGSRC)->msd->flags, \ &(FLAGSRC)->msd->cmp_less); \ } while (0) if (!a->msd->root) if (operation & PIKE_ARRAY_OP_B) /* Result is b. */ if (operation & PIKE_MERGE_DESTR_A) { if (a->node_refs) return NULL; COPY_MSD_AND_KEEP_FLAGS (b, a); return a; } else { struct multiset *res; ALLOC_COPY_AND_SET_FLAGS (b, res, a); return res; } else /* Result is empty. */ if (operation & PIKE_MERGE_DESTR_A) return a; else { struct multiset *res; ALLOC_EMPTY_AND_SET_FLAGS (res, a); return res; } else if (!b->msd->root) if (operation & (PIKE_ARRAY_OP_A << 8)) /* Result is a. */ if (operation & PIKE_MERGE_DESTR_A) return a; else { struct multiset *res; ALLOC_COPY_AND_SET_FLAGS (a, res, a); return res; } else /* Result is empty. */ if (operation & PIKE_MERGE_DESTR_A) { if (a->node_refs) return NULL; EMPTY_MSD_AND_KEEP_FLAGS (a); return a; } else { struct multiset *res; ALLOC_EMPTY_AND_SET_FLAGS (res, a); return res; } else if (a == b) { op = operation & ((PIKE_ARRAY_OP_A|PIKE_ARRAY_OP_B) << 4); if (op) { if (op != ((PIKE_ARRAY_OP_A|PIKE_ARRAY_OP_B) << 4)) { /* Result is a (or b). */ if (operation & PIKE_MERGE_DESTR_A) return a; else { struct multiset *res; ALLOC_COPY_AND_SET_FLAGS (a, res, a); return res; } } } else /* Result is empty. */ if (operation & PIKE_MERGE_DESTR_A) { if (a->node_refs) return NULL; EMPTY_MSD_AND_KEEP_FLAGS (a); return a; } else { struct multiset *res; ALLOC_EMPTY_AND_SET_FLAGS (res, a); return res; } } return NULL; } struct merge_data { struct multiset *a, *b, *res, *tmp; struct recovery_data rd; struct rb_node_hdr *a_node, *b_node, *res_list; size_t res_length; }; static void cleanup_merge_data (struct merge_data *m) { debug_malloc_touch (m->a); debug_malloc_touch (m->a ? m->a->msd : NULL); debug_malloc_touch (m->b); debug_malloc_touch (m->b ? m->b->msd : NULL); debug_malloc_touch (m->res); debug_malloc_touch (m->res ? m->res->msd : NULL); debug_malloc_touch (m->tmp); debug_malloc_touch (m->tmp ? m->tmp->msd : NULL); if (m->res_list) { /* The result msd contains a list and possibly a part of a tree. * Knit it together to a tree again. Knowledge that LOW_RB_MERGE * traverses the trees backwards is used here. */ struct rb_node_hdr *list = m->res_list; size_t length = m->res_length; if (m->res == m->a) { struct rb_node_hdr *node; for (node = m->a_node; node; node = rb_prev (node)) { node->next = list, list = node; length++; } } m->res->msd->root = RBNODE (rb_make_tree (list, length)); } sub_msnode_ref (m->res); if (m->res != m->a) free_multiset (m->res); if (m->tmp) free_multiset (m->tmp); free_recovery_data (&m->rd); } static void merge_shift_ptrs (struct multiset_data *old, struct multiset_data *new, struct merge_data *m) { if (m->a == m->res && m->a_node) m->a_node = SHIFT_HDRPTR (m->a_node, old, new); if (m->res_list) m->res_list = SHIFT_HDRPTR (m->res_list, old, new); } /* If PIKE_MERGE_DESTR_A is used, a is returned without ref increase. * Else the new multiset will inherit flags and cmp_less from a. * * If destructive on an operand and there is an exception, then some * random part(s) of the operand will be left unprocessed. All entries * that were in the operand and would remain in the finished result * will still be there, and no entries from the other operand that * wouldn't be in the finished result. */ PMOD_EXPORT struct multiset *merge_multisets (struct multiset *a, struct multiset *b, int operation) { struct merge_data m;
f84a5a2015-04-26Henrik Grubbström (Grubba)  int got_node_refs;
5b15bb2001-12-10Martin Stjernholm  TYPE_FIELD ind_types, val_types; ONERROR uwp; debug_malloc_touch (a); debug_malloc_touch (a->msd); debug_malloc_touch (b); debug_malloc_touch (b->msd); #ifdef PIKE_DEBUG if (operation & PIKE_MERGE_DESTR_B)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Destructiveness on second operand not supported.\n");
5b15bb2001-12-10Martin Stjernholm #endif #if 1 if (!a->msd->root || !b->msd->root || a == b) { struct multiset *res = merge_special (a, b, operation); if (res) return res; } #endif m.tmp = NULL; m.res_list = NULL; m.res_length = 0; /* Preparations. Set m.res and make sure the operands have the same * form. This can do up to three multiset copies that could be * optimized away, but that'd lead to quite a bit of extra code and * those situations are so unusual it's not worth bothering * about. */ if (operation & PIKE_MERGE_DESTR_A) { #ifdef PIKE_DEBUG if (a->refs != 1)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Not safe to do destructive merge with several refs to the operand.\n");
5b15bb2001-12-10Martin Stjernholm #endif m.res = m.a = a; if (a == b) /* Can't handle the result being the same as both a and b. */ m.b = m.tmp = copy_multiset (b); else m.b = b; /* Can't handle a shared data block even though there might be no * change in it, since the merge always relinks the tree. */ prepare_for_change (m.res, got_node_refs = m.res->node_refs); } else { int newsize; if (operation & (PIKE_ARRAY_OP_A << 8)) newsize = multiset_sizeof (a); else newsize = 0; if (operation & PIKE_ARRAY_OP_B) newsize += multiset_sizeof (b); m.res = allocate_multiset (newsize, a->msd->flags, &a->msd->cmp_less); m.a = a, m.b = b; got_node_refs = 0; } if (!SAME_CMP_LESS (a->msd, b->msd)) { if (!m.tmp) m.b = m.tmp = copy_multiset (b);
7d61ba2008-05-30Martin Stjernholm  multiset_set_cmp_less (m.b, &a->msd->cmp_less);
5b15bb2001-12-10Martin Stjernholm  } ind_types = val_types = 0; if (m.res == a) m.rd.a_msd = NULL; else add_ref (m.rd.a_msd = m.a->msd); add_ref (m.rd.b_msd = m.b->msd); add_msnode_ref (m.res); SET_ONERROR (uwp, cleanup_merge_data, &m); #define ALLOC_RES_NODE(RES, RES_MSD, NEW_NODE) \ do { \ union msnode *node; \ if (prepare_for_add (RES, 1)) { \ merge_shift_ptrs (RES_MSD, (RES)->msd, &m); \ (RES_MSD) = (RES)->msd; \ } \ ALLOC_MSNODE (RES_MSD, (RES)->node_refs, node); \ NEW_NODE = HDR (node); \ } while (0) #define UNLINK_RES_NODE(RES_MSD, RES_LIST, GOT_NODE_REFS, NODE) \ do { \ if (GOT_NODE_REFS) { \
b3c3492001-12-10Martin Stjernholm  ADD_TO_FREE_LIST (RES_MSD, RBNODE (NODE)); \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(RBNODE (NODE)->i.ind, T_DELETED); \
5b15bb2001-12-10Martin Stjernholm  /* Knowledge that LOW_RB_MERGE traverses the trees backwards */ \ /* is used here. */ \ RBNODE (NODE)->i.ind.u.ptr = RES_LIST; \ RBNODE (NODE)->i.prev = \ (struct msnode_ind *) low_multiset_prev (RBNODE (NODE)); \ } \ else { \
b3c3492001-12-10Martin Stjernholm  CLEAR_DELETED_ON_FREE_LIST (RES_MSD); \ ADD_TO_FREE_LIST (RES_MSD, RBNODE (NODE)); \
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(RBNODE (NODE)->i.ind, PIKE_T_UNKNOWN); \
5b15bb2001-12-10Martin Stjernholm  RBNODE (NODE)->i.prev = NULL; \ RES_MSD->size--; \ } \ } while (0)
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(m.res->msd->cmp_less) == T_INT) {
5b15bb2001-12-10Martin Stjernholm  struct multiset_data *res_msd = m.res->msd; struct svalue a_ind, b_ind; m.a_node = HDR (m.a->msd->root), m.b_node = HDR (m.rd.b_msd->root); if (m.rd.a_msd) /* Not destructive on a. */ LOW_RB_MERGE ( ic_nd, m.a_node, m.b_node, m.res_list, m.res_length, operation, { low_use_multiset_index (RBNODE (m.a_node), a_ind); if (IS_DESTRUCTED (&a_ind)) goto ic_nd_free_a; }, { low_use_multiset_index (RBNODE (m.b_node), b_ind); if (IS_DESTRUCTED (&b_ind)) goto ic_nd_free_b; },
5625622011-03-06Martin Stjernholm  { INTERNAL_CMP (&a_ind, &b_ind, cmp_res); },
5b15bb2001-12-10Martin Stjernholm  { /* Copy m.a_node. */ ALLOC_RES_NODE (m.res, res_msd, new_node); assign_svalue_no_free (&RBNODE (new_node)->i.ind, &a_ind);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(a_ind); DO_IF_DEBUG (SET_SVAL_TYPE(RBNODE (new_node)->i.ind, TYPEOF(RBNODE (new_node)->i.ind) | MULTISET_FLAG_MARKER));
5b15bb2001-12-10Martin Stjernholm  }, { /* Free m.a_node. */ }, { /* Copy m.b_node. */ ALLOC_RES_NODE (m.res, res_msd, new_node); assign_svalue_no_free (&RBNODE (new_node)->i.ind, &b_ind);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(b_ind); DO_IF_DEBUG (SET_SVAL_TYPE(RBNODE (new_node)->i.ind, TYPEOF(RBNODE (new_node)->i.ind) | MULTISET_FLAG_MARKER));
5b15bb2001-12-10Martin Stjernholm  }, { /* Free m.b_node. */ }); else /* Destructive on a. */ LOW_RB_MERGE ( ic_da, m.a_node, m.b_node, m.res_list, m.res_length, operation, { low_use_multiset_index (RBNODE (m.a_node), a_ind); if (IS_DESTRUCTED (&a_ind)) goto ic_da_free_a; }, { low_use_multiset_index (RBNODE (m.b_node), b_ind); if (IS_DESTRUCTED (&b_ind)) goto ic_da_free_b; },
5625622011-03-06Martin Stjernholm  { INTERNAL_CMP (&a_ind, &b_ind, cmp_res); },
5b15bb2001-12-10Martin Stjernholm  { /* Copy m.a_node. */ new_node = m.a_node;
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(a_ind);
5b15bb2001-12-10Martin Stjernholm  }, { /* Free m.a_node. */ free_svalue (&a_ind); UNLINK_RES_NODE (res_msd, m.res_list, got_node_refs, m.a_node); }, { /* Copy m.b_node. */ ALLOC_RES_NODE (m.res, res_msd, new_node); assign_svalue_no_free (&RBNODE (new_node)->i.ind, &b_ind);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(b_ind); DO_IF_DEBUG (SET_SVAL_TYPE(RBNODE (new_node)->i.ind, TYPEOF(RBNODE (new_node)->i.ind) | MULTISET_FLAG_MARKER));
5b15bb2001-12-10Martin Stjernholm  }, { /* Free m.b_node. */ }); } else { struct svalue *cmp_less = &m.res->msd->cmp_less; struct multiset_data *res_msd = m.res->msd; struct svalue a_ind, b_ind;
b0d15b2002-09-01Martin Stjernholm  Pike_fatal ("TODO: Merge of multisets with external sort function "
5b15bb2001-12-10Martin Stjernholm  "not yet implemented.\n"); LOW_RB_MERGE ( ec, m.a_node, m.b_node, m.res_list, m.res_length, operation, { low_use_multiset_index (RBNODE (m.a_node), a_ind); if (IS_DESTRUCTED (&a_ind)) goto ec_free_a; }, { low_use_multiset_index (RBNODE (m.b_node), b_ind); if (IS_DESTRUCTED (&b_ind)) goto ec_free_b; }, { push_svalue (&a_ind); push_svalue (&b_ind); EXTERNAL_CMP (cmp_less);
19961b2017-04-08Martin Nilsson  cmp_res = UNSAFE_IS_ZERO (Pike_sp - 1) ? 0 : -1;
5b15bb2001-12-10Martin Stjernholm  pop_stack(); if (!cmp_res) { push_svalue (&b_ind); push_svalue (&a_ind); EXTERNAL_CMP (cmp_less);
19961b2017-04-08Martin Nilsson  cmp_res = UNSAFE_IS_ZERO (Pike_sp - 1) ? 0 : 1;
5b15bb2001-12-10Martin Stjernholm  pop_stack(); if (!cmp_res) { /* The values are orderwise equal. Have to check if there * is an orderwise equal sequence in either operand, since * we must do an array-like merge between them in that * case. Knowledge that LOW_RB_MERGE traverses the trees * backwards is used here. */
b0d15b2002-09-01Martin Stjernholm  /* TODO */
5b15bb2001-12-10Martin Stjernholm  } } }, { /* Copy m.a_node. */ if (m.rd.a_msd) { ALLOC_RES_NODE (m.res, res_msd, new_node); assign_svalue_no_free (&RBNODE (new_node)->i.ind, &a_ind);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(a_ind); DO_IF_DEBUG (SET_SVAL_TYPE(RBNODE (new_node)->i.ind, TYPEOF(RBNODE (new_node)->i.ind) | MULTISET_FLAG_MARKER));
5b15bb2001-12-10Martin Stjernholm  } else new_node = m.a_node; }, { /* Free m.a_node. */ if (m.rd.a_msd) {} else { free_svalue (&a_ind); UNLINK_RES_NODE (res_msd, m.res_list, got_node_refs, m.a_node); } }, { /* Copy m.b_node. */ ALLOC_RES_NODE (m.res, res_msd, new_node); assign_svalue_no_free (&RBNODE (new_node)->i.ind, &b_ind);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(b_ind); DO_IF_DEBUG (SET_SVAL_TYPE(RBNODE (new_node)->i.ind, TYPEOF(RBNODE (new_node)->i.ind) | MULTISET_FLAG_MARKER));
5b15bb2001-12-10Martin Stjernholm  }, { /* Free m.b_node. */ }); } #undef ALLOC_RES_NODE #undef UNLINK_RES_NODE #ifdef PIKE_DEBUG if (operation & PIKE_MERGE_DESTR_A) { if (a->refs != 1)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("First operand grew external refs during destructive merge.\n");
5b15bb2001-12-10Martin Stjernholm  if (a->msd->refs > 1)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Data block of first operand grew external refs "
5b15bb2001-12-10Martin Stjernholm  "during destructive merge.\n"); } #endif UNSET_ONERROR (uwp); m.res->msd->root = RBNODE (rb_make_tree (m.res_list, m.res_length)); m.res->msd->ind_types = ind_types; if (m.tmp) free_multiset (m.tmp); if (m.rd.a_msd && !sub_ref (m.rd.a_msd)) free_multiset_data (m.rd.a_msd); if (!sub_ref (m.rd.b_msd)) free_multiset_data (m.rd.b_msd); #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (m.res, 1);
5b15bb2001-12-10Martin Stjernholm #endif sub_msnode_ref (m.res); /* Tries to shrink m.res->msd. */ return m.res; } /* The result has values iff any argument has values. The order is * taken from the first argument. No weak flags are propagated to * the result. */ PMOD_EXPORT struct multiset *add_multisets (struct svalue *vect, int count) { struct multiset *res, *l;
7fef7b2012-06-27Martin Stjernholm  int size = 0, idx;
5b15bb2001-12-10Martin Stjernholm  struct svalue *cmp_less = count ? &vect[0].u.multiset->msd->cmp_less : NULL; ONERROR uwp; for (idx = 0; idx < count; idx++) { struct multiset *l = vect[idx].u.multiset; debug_malloc_touch (l); debug_malloc_touch (l->msd); size += multiset_sizeof (l); }
7d61ba2008-05-30Martin Stjernholm  if (!size)
f84a5a2015-04-26Henrik Grubbström (Grubba)  return allocate_multiset (0, 0, cmp_less);
5b15bb2001-12-10Martin Stjernholm  for (idx = 0;; idx++) { l = vect[idx].u.multiset; if (l->msd->root) break; }
f84a5a2015-04-26Henrik Grubbström (Grubba)  if ((cmp_less ?
7d61ba2008-05-30Martin Stjernholm  is_identical (cmp_less, &l->msd->cmp_less) :
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(l->msd->cmp_less) == T_INT)) {
5b15bb2001-12-10Martin Stjernholm  res = copy_multiset (l);
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_set_flags (res, 0);
5b15bb2001-12-10Martin Stjernholm  idx++; } else
f84a5a2015-04-26Henrik Grubbström (Grubba)  res = allocate_multiset (size, 0, cmp_less);
5b15bb2001-12-10Martin Stjernholm  SET_ONERROR (uwp, do_free_multiset, res); for (; idx < count; idx++)
b0d15b2002-09-01Martin Stjernholm  /* TODO: This is inefficient as long as merge_multisets
5b15bb2001-12-10Martin Stjernholm  * always is linear. */ merge_multisets (res, vect[idx].u.multiset, PIKE_MERGE_DESTR_A | PIKE_ARRAY_OP_ADD); UNSET_ONERROR (uwp); return res; } /* Differences in the weak flags are ignored, but not the order
b0d15b2002-09-01Martin Stjernholm  * function and whether there are values or not. Since the order * always is well defined - even in the parts of the multisets where * the order function doesn't define it - it's also always * significant. */
5b15bb2001-12-10Martin Stjernholm PMOD_EXPORT int multiset_equal_p (struct multiset *a, struct multiset *b, struct processing *p) { struct processing curr; struct recovery_data rd; union msnode *a_node, *b_node; struct svalue a_ind, b_ind; int res; ONERROR uwp; debug_malloc_touch (a); debug_malloc_touch (a->msd); if (a == b) return 1; debug_malloc_touch (b); debug_malloc_touch (b->msd);
dd1b0d2003-08-20Henrik Grubbström (Grubba)  if (a->msd == b->msd) return 1;
5b15bb2001-12-10Martin Stjernholm  check_multiset_for_destruct (a); check_multiset_for_destruct (b); rd.a_msd = a->msd, rd.b_msd = b->msd; if (multiset_sizeof (a) != multiset_sizeof (b) ||
dd1b0d2003-08-20Henrik Grubbström (Grubba)  rd.a_msd->flags || rd.b_msd->flags ||
5b15bb2001-12-10Martin Stjernholm  !SAME_CMP_LESS (rd.a_msd, rd.b_msd)) return 0;
283af72017-10-09Henrik Grubbström (Grubba)  if (!rd.a_msd->root) return !rd.b_msd->root; if (!rd.b_msd->root) return 0;
5b15bb2001-12-10Martin Stjernholm  curr.pointer_a = (void *) a; curr.pointer_b = (void *) b; curr.next = p; for (; p; p = p->next) if (p->pointer_a == (void *) a && p->pointer_b == (void *) b) return 1; add_ref (rd.a_msd); add_ref (rd.b_msd); SET_ONERROR (uwp, free_recovery_data, &rd); a_node = low_multiset_first (rd.a_msd); b_node = low_multiset_first (rd.b_msd); res = 1;
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  do { \
f84a5a2015-04-26Henrik Grubbström (Grubba)  if (!low_is_equal (low_use_multiset_index (a_node, a_ind), \
5b15bb2001-12-10Martin Stjernholm  low_use_multiset_index (b_node, b_ind), \ &curr)) { \ res = 0; \ break; \ } \ a_node = low_multiset_next (a_node); \ b_node = low_multiset_next (b_node); \ } while (a_node); DO_WITH_NODES (rd.a_msd); #undef WITH_NODES_BLOCK UNSET_ONERROR (uwp); if (!sub_ref (rd.a_msd)) free_multiset_data (rd.a_msd); if (!sub_ref (rd.b_msd)) free_multiset_data (rd.b_msd); return res; }
e0cd662016-12-28Arne Goedeke void describe_multiset (struct byte_buffer *b, struct multiset *l, struct processing *p, int indent)
5b15bb2001-12-10Martin Stjernholm { struct processing curr; struct multiset_data *msd; int depth; debug_malloc_touch (l); debug_malloc_touch (l->msd); curr.pointer_a = (void *) l; curr.next = p; for (depth = 0; p; p = p->next, depth++) if (p->pointer_a == (void *) l) { char buf[20]; sprintf (buf, "@%d", depth);
e0cd662016-12-28Arne Goedeke  buffer_add_str (b, buf);
5b15bb2001-12-10Martin Stjernholm  return; } check_multiset_for_destruct (l); msd = l->msd; if (!msd->root)
e0cd662016-12-28Arne Goedeke  buffer_add_str (b, "(< >)");
5b15bb2001-12-10Martin Stjernholm  else { union msnode *node; struct svalue ind; INT32 size = multiset_sizeof (l); int notfirst = 0; ONERROR uwp; if (size == 1)
e0cd662016-12-28Arne Goedeke  buffer_add_str (b, "(< /* 1 element */\n");
5b15bb2001-12-10Martin Stjernholm  else { char buf[40]; sprintf (buf, "(< /* %ld elements */\n", (long) size);
e0cd662016-12-28Arne Goedeke  buffer_add_str (b, buf);
5b15bb2001-12-10Martin Stjernholm  } indent += 2; add_ref (msd); SET_ONERROR (uwp, free_indirect_multiset_data, &msd); node = low_multiset_first (msd);
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  do { \
e0cd662016-12-28Arne Goedeke  if (notfirst) buffer_add_str (b, ",\n"); \
5b15bb2001-12-10Martin Stjernholm  else notfirst = 1; \ \
e0cd662016-12-28Arne Goedeke  for (depth = 2; depth < indent; depth++) buffer_add_char (b, ' ');\
5b15bb2001-12-10Martin Stjernholm  low_use_multiset_index (node, ind); \
e0cd662016-12-28Arne Goedeke  describe_svalue (b, &ind, indent, &curr); \
5b15bb2001-12-10Martin Stjernholm  } while ((node = low_multiset_next (node))); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK
e0cd662016-12-28Arne Goedeke  buffer_add_char (b, '\n'); for (depth = 4; depth < indent; depth++) buffer_add_char (b, ' '); buffer_add_str (b, ">)");
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); if (!sub_ref (msd)) free_multiset_data (msd); } } int multiset_is_constant (struct multiset *l, struct processing *p) { struct multiset_data *msd = l->msd; int res = 1; debug_malloc_touch (l); debug_malloc_touch (msd); if (msd->root && (msd->ind_types | msd->val_types) & ~(BIT_INT|BIT_FLOAT|BIT_STRING)) { union msnode *node = low_multiset_first (msd); struct svalue ind; add_ref (msd);
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5b15bb2001-12-10Martin Stjernholm  do { \
f84a5a2015-04-26Henrik Grubbström (Grubba)  if (!svalues_are_constant (low_use_multiset_index (node, ind), \
5b15bb2001-12-10Martin Stjernholm  1, msd->ind_types, p)) { \ res = 0; \ break; \ } \ } while ((node = low_multiset_next (node))); DO_WITH_NODES (msd); #undef WITH_NODES_BLOCK sub_extra_ref (msd); assert (msd->refs); } return res; } node *make_node_from_multiset (struct multiset *l) { debug_malloc_touch (l); debug_malloc_touch (l->msd); multiset_fix_type_field (l); if (multiset_is_constant (l, NULL)) { struct svalue s; if (!l->msd->root) return mkefuncallnode ("aggregate_multiset", NULL);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(s, T_MULTISET, 0, multiset, l);
5b15bb2001-12-10Martin Stjernholm  return mkconstantsvaluenode (&s); } else { struct array *ind = multiset_range_indices (l, -1, -1); node *n; #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(l->msd->cmp_less) != T_INT)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Didn't expect multiset with custom order function.\n");
5b15bb2001-12-10Martin Stjernholm  if (l->msd->flags & MULTISET_WEAK)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Didn't expect multiset with weak flag(s).\n");
5b15bb2001-12-10Martin Stjernholm #endif
f84a5a2015-04-26Henrik Grubbström (Grubba)  n = mkefuncallnode ("mkmultiset", make_node_from_array (ind));
5b15bb2001-12-10Martin Stjernholm  free_array (ind); return n; } }
9993a42007-12-27Martin Nilsson /*! @decl multiset aggregate_multiset(mixed ... elems) *! *! Construct a multiset with the arguments as indices. The multiset *! will not contain any values. This method is most useful when *! constructing multisets with @[map] or similar; generally, the *! multiset literal syntax is handier: @expr{(<elem1, elem2, ...>)@} *! With it, it's also possible to construct a multiset with values: *! @expr{(<index1: value1, index2: value2, ...>)@} *! *! @seealso *! @[sizeof()], @[multisetp()], @[mkmultiset()]
5b15bb2001-12-10Martin Stjernholm  */ PMOD_EXPORT void f_aggregate_multiset (INT32 args) { f_aggregate (args);
19961b2017-04-08Martin Nilsson  push_multiset (mkmultiset_2 (Pike_sp[-1].u.array, NULL)); free_array (Pike_sp[-2].u.array); Pike_sp[-2] = Pike_sp[-1];
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
19961b2017-04-08Martin Nilsson  Pike_sp--;
5b15bb2001-12-10Martin Stjernholm } struct multiset *copy_multiset_recursively (struct multiset *l,
9cc28d2004-05-28Henrik Grubbström (Grubba)  struct mapping *p)
5b15bb2001-12-10Martin Stjernholm {
65dc532010-07-11Martin Stjernholm  int not_complex;
5b15bb2001-12-10Martin Stjernholm  struct tree_build_data new; struct multiset_data *msd = l->msd; union msnode *node;
f84a5a2015-04-26Henrik Grubbström (Grubba)  int pos;
5b15bb2001-12-10Martin Stjernholm  struct svalue ind; TYPE_FIELD ind_types, val_types; ONERROR uwp; debug_malloc_touch (l); debug_malloc_touch (msd); #ifdef PIKE_DEBUG if (d_flag > 1) check_multiset_type_fields (l); #endif
65dc532010-07-11Martin Stjernholm  if (msd->root && ((msd->ind_types | msd->val_types) & BIT_COMPLEX)) { not_complex = 0; /* Use a dummy empty msd temporarily in the new multiset, since the * real one is not suitable for general consumption while it's being * built below. This will have the effect that any changes in the * multiset made by other code during the build will change the * dummy msd and will thus be lost afterwards. */ new.l = allocate_multiset (0, msd->flags, &msd->cmp_less); new.msd = low_alloc_multiset_data (multiset_sizeof (l), msd->flags); assign_svalue_no_free (&new.msd->cmp_less, &msd->cmp_less); ind_types = 0;
f84a5a2015-04-26Henrik Grubbström (Grubba)  val_types = BIT_INT;
65dc532010-07-11Martin Stjernholm  add_ref (new.msd2 = msd); SET_ONERROR (uwp, free_tree_build_data, &new); } else { not_complex = 1; new.l = copy_multiset (l); }
5b15bb2001-12-10Martin Stjernholm 
0f5a822010-07-11Jonas Wallden  if (p) {
65dc532010-07-11Martin Stjernholm  struct svalue aa, bb;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(aa, T_MULTISET, 0, multiset, l); SET_SVAL(bb, T_MULTISET, 0, multiset, new.l);
0f5a822010-07-11Jonas Wallden  mapping_insert(p, &aa, &bb); }
9cc28d2004-05-28Henrik Grubbström (Grubba) 
65dc532010-07-11Martin Stjernholm  if (not_complex) return new.l;
13d0782004-05-19Martin Stjernholm  node = low_multiset_first (msd); pos = 0; do {
f84a5a2015-04-26Henrik Grubbström (Grubba)  new.node = INODE (NODE_AT (new.msd, msnode_ind, pos));
13d0782004-05-19Martin Stjernholm  pos++;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(new.node->i.ind, T_INT); SET_SVAL_SUBTYPE(new.node->i.ind, NUMBER_NUMBER);
13d0782004-05-19Martin Stjernholm 
7790632004-06-13Martin Stjernholm  low_use_multiset_index (node, ind); if (!IS_DESTRUCTED (&ind)) { copy_svalues_recursively_no_free (&new.node->i.ind, &ind, 1, p);
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << TYPEOF(new.node->i.ind);
5b15bb2001-12-10Martin Stjernholm 
7790632004-06-13Martin Stjernholm  /* Note: Similar code in multiset_set_cmp_less and mkmultiset_2. */
13d0782004-05-19Martin Stjernholm 
7790632004-06-13Martin Stjernholm  while (1) { RBSTACK_INIT (rbstack);
13d0782004-05-19Martin Stjernholm 
7790632004-06-13Martin Stjernholm  if (!new.msd->root) { low_rb_init_root (HDR (new.msd->root = new.node));
13d0782004-05-19Martin Stjernholm  goto node_added;
7790632004-06-13Martin Stjernholm  } switch (low_multiset_track_le_gt (new.msd,
a503492009-04-06Martin Stjernholm  /* Not clobbered yet. */ &new.node->i.ind,
7790632004-06-13Martin Stjernholm  &rbstack)) { case FIND_LESS:
a503492009-04-06Martin Stjernholm  low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.node));
7790632004-06-13Martin Stjernholm  goto node_added; case FIND_GREATER:
a503492009-04-06Martin Stjernholm  low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.node));
7790632004-06-13Martin Stjernholm  goto node_added; case FIND_DESTRUCTED: midflight_remove_node_faster (new.msd, rbstack); break;
a503492009-04-06Martin Stjernholm  case FIND_KEY_DESTRUCTED: RBSTACK_FREE (rbstack); goto node_skipped;
7790632004-06-13Martin Stjernholm  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n")); }
13d0782004-05-19Martin Stjernholm  }
b42e942015-09-28Martin Nilsson  UNREACHABLE();
13d0782004-05-19Martin Stjernholm 
7790632004-06-13Martin Stjernholm  node_added:
13d0782004-05-19Martin Stjernholm #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(new.node->i.ind, TYPEOF(new.node->i.ind) | MULTISET_FLAG_MARKER);
13d0782004-05-19Martin Stjernholm #endif
7790632004-06-13Martin Stjernholm  new.msd->size++; }
a503492009-04-06Martin Stjernholm  node_skipped:;
13d0782004-05-19Martin Stjernholm  } while ((node = low_multiset_next (node))); new.msd->ind_types = ind_types; new.msd->val_types = val_types;
5b15bb2001-12-10Martin Stjernholm  UNSET_ONERROR (uwp); if (!sub_ref (msd)) free_multiset_data (msd); assert (!new.msd->refs); fix_free_list (new.msd, pos); if (!sub_ref (new.l->msd)) free_multiset_data (new.l->msd); add_ref (new.l->msd = new.msd); return new.l; } /* Does not handle n being too large. */ PMOD_EXPORT ptrdiff_t multiset_get_nth (struct multiset *l, size_t n) { add_msnode_ref (l); return MSNODE2OFF (l->msd, RBNODE (rb_get_nth (HDR (l->msd->root), n))); }
5e83442008-05-11Martin Stjernholm 
f10a472008-05-06Martin Stjernholm #define GC_MSD_VISITED GC_USER_1 #define GC_MSD_GOT_EXT_REFS GC_USER_2 #define GC_MSD_GOT_NODE_REFS GC_USER_3
5b15bb2001-12-10Martin Stjernholm 
5e83442008-05-11Martin Stjernholm static void visit_multiset_data (struct multiset_data *msd, int action,
d056542014-06-17Henrik Grubbström (Grubba)  void *extra)
5e83442008-05-11Martin Stjernholm {
c42e092014-06-18Henrik Grubbström (Grubba)  visit_enter(msd, T_MULTISET_DATA, extra);
8775df2015-06-04Martin Karlgren  switch (action & VISIT_MODE_MASK) {
5e83442008-05-11Martin Stjernholm #ifdef PIKE_DEBUG default: Pike_fatal ("Unknown visit action %d.\n", action); case VISIT_NORMAL: case VISIT_COMPLEX_ONLY: break; #endif case VISIT_COUNT_BYTES:
f84a5a2015-04-26Henrik Grubbström (Grubba)  mc_counted_bytes += (NODE_OFFSET (msnode_ind, msd->allocsize));
5e83442008-05-11Martin Stjernholm  break; }
8775df2015-06-04Martin Karlgren  if (!(action & VISIT_NO_REFS) && msd->root &&
5e83442008-05-11Martin Stjernholm  ((msd->ind_types | msd->val_types) & (action & VISIT_COMPLEX_ONLY ? BIT_COMPLEX : BIT_REF_TYPES))) { int ind_ref_type = msd->flags & MULTISET_WEAK_INDICES ? REF_TYPE_WEAK : REF_TYPE_NORMAL; union msnode *node = low_multiset_first (msd); struct svalue ind;
f84a5a2015-04-26Henrik Grubbström (Grubba)  do { low_use_multiset_index (node, ind); visit_svalue (&ind, ind_ref_type, extra); } while ((node = low_multiset_next (node)));
5e83442008-05-11Martin Stjernholm  }
c42e092014-06-18Henrik Grubbström (Grubba)  visit_leave(msd, T_MULTISET_DATA, extra);
5e83442008-05-11Martin Stjernholm }
d056542014-06-17Henrik Grubbström (Grubba) PMOD_EXPORT void visit_multiset (struct multiset *l, int action, void *extra)
5e83442008-05-11Martin Stjernholm {
c42e092014-06-18Henrik Grubbström (Grubba)  visit_enter(l, T_MULTISET, extra);
8775df2015-06-04Martin Karlgren  switch (action & VISIT_MODE_MASK) {
5e83442008-05-11Martin Stjernholm #ifdef PIKE_DEBUG default: Pike_fatal ("Unknown visit action %d.\n", action); case VISIT_NORMAL: case VISIT_COMPLEX_ONLY: break; #endif case VISIT_COUNT_BYTES: mc_counted_bytes += sizeof (struct multiset); break; } visit_ref (l->msd, REF_TYPE_INTERNAL,
d056542014-06-17Henrik Grubbström (Grubba)  (visit_thing_fn *) &visit_multiset_data, extra);
c42e092014-06-18Henrik Grubbström (Grubba)  visit_leave(l, T_MULTISET, extra);
5e83442008-05-11Martin Stjernholm }
5b15bb2001-12-10Martin Stjernholm unsigned gc_touch_all_multisets (void) { unsigned n = 0; struct multiset *l; if (first_multiset && first_multiset->prev)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Error in multiset link list.\n");
5b15bb2001-12-10Martin Stjernholm  for (l = first_multiset; l; l = l->next) { debug_gc_touch (l); n++; if (l->next && l->next->prev != l)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Error in multiset link list.\n");
5b15bb2001-12-10Martin Stjernholm  } return n; } void gc_check_all_multisets (void) { struct multiset *l; /* Loop twice: First to get the number of internal refs to the msd:s * right, and then again to check the svalues in them correctly. * This is necessary since we need to know if an msd got external * direct refs to avoid checking its svalues as weak. */ for (l = first_multiset; l; l = l->next) { struct multiset_data *msd = l->msd; #ifdef DEBUG_MALLOC
ad8d052008-05-02Martin Stjernholm  if (((int) PTR_TO_INT (msd)) == 0x55555555) {
5b15bb2001-12-10Martin Stjernholm  fprintf (stderr, "** Zapped multiset in list of active multisets.\n"); describe_something (l, T_MULTISET, 0, 2, 0, NULL);
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Zapped multiset in list of active multisets.\n");
5b15bb2001-12-10Martin Stjernholm  } #endif #ifdef PIKE_DEBUG
fd04632002-11-23Martin Stjernholm  if (d_flag > 1) check_multiset (l, 1);
5b15bb2001-12-10Martin Stjernholm #endif
e1a35e2003-09-08Martin Stjernholm  GC_ENTER (l, T_MULTISET) { debug_gc_check (msd, " as multiset data block of a multiset"); } GC_LEAVE;
5b15bb2001-12-10Martin Stjernholm  }
5e83442008-05-11Martin Stjernholm  for (l = first_multiset; l; l = l->next) { struct multiset_data *msd = l->msd; struct marker *m = get_marker (msd);
ad8d052008-05-02Martin Stjernholm 
abe64b2018-05-19Tobias S. Josefowitz  if (!(m->gc_flags & GC_MSD_VISITED))
5e83442008-05-11Martin Stjernholm  GC_ENTER (l, T_MULTISET) {
abe64b2018-05-19Tobias S. Josefowitz  if (m->gc_refs < msd->refs) m->gc_flags |= GC_MSD_GOT_EXT_REFS;
ad8d052008-05-02Martin Stjernholm 
5e83442008-05-11Martin Stjernholm  if (msd->root) { union msnode *node = low_multiset_first (msd); struct svalue ind;
5b15bb2001-12-10Martin Stjernholm 
f84a5a2015-04-26Henrik Grubbström (Grubba) #define WITH_NODES_BLOCK(TYPE, IND) \
5e83442008-05-11Martin Stjernholm  if (!(msd->flags & MULTISET_WEAK) || \
abe64b2018-05-19Tobias S. Josefowitz  (m->gc_flags & GC_MSD_GOT_EXT_REFS)) \
5e83442008-05-11Martin Stjernholm  do { \ low_use_multiset_index (node, ind); \ debug_gc_check_svalues (&ind, 1, " as multiset index"); \ } while ((node = low_multiset_next (node))); \
5b15bb2001-12-10Martin Stjernholm  \
5e83442008-05-11Martin Stjernholm  else { \ switch (msd->flags & MULTISET_WEAK) { \ case MULTISET_WEAK_INDICES: \ do { \ low_use_multiset_index (node, ind); \ debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \ } while ((node = low_multiset_next (node))); \ break; \
5b15bb2001-12-10Martin Stjernholm  \
b684132014-05-05Per Hedbor  /* case MULTISET_WEAK_VALUES: \
5e83442008-05-11Martin Stjernholm  do { \ low_use_multiset_index (node, ind); \ debug_gc_check_svalues (&ind, 1, " as multiset index"); \ } while ((node = low_multiset_next (node))); \
b684132014-05-05Per Hedbor  break; \
5b15bb2001-12-10Martin Stjernholm  \
5e83442008-05-11Martin Stjernholm  default: \ do { \ low_use_multiset_index (node, ind); \ debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \ } while ((node = low_multiset_next (node))); \
b684132014-05-05Per Hedbor  break;*/ \
5e83442008-05-11Martin Stjernholm  } \ gc_checked_as_weak (msd); \ }
5b15bb2001-12-10Martin Stjernholm 
5e83442008-05-11Martin Stjernholm  DO_WITH_NODES (msd);
5b15bb2001-12-10Martin Stjernholm  #undef WITH_NODES_BLOCK
5e83442008-05-11Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm 
abe64b2018-05-19Tobias S. Josefowitz  if (l->node_refs) m->gc_flags |= GC_MSD_GOT_NODE_REFS | GC_MSD_VISITED; else m->gc_flags |= GC_MSD_VISITED;
5e83442008-05-11Martin Stjernholm  } GC_LEAVE; }
5b15bb2001-12-10Martin Stjernholm } static void gc_unlink_msnode_shared (struct multiset_data *msd, struct rbstack_ptr *track, int got_node_refs) { struct rbstack_ptr rbstack = *track; union msnode *unlinked_node; /* Note: Similar code in unlink_msnode. */ if (got_node_refs) { union msnode *prev, *next; unlinked_node = RBNODE (RBSTACK_PEEK (rbstack)); prev = low_multiset_prev (unlinked_node); next = low_multiset_next (unlinked_node); low_rb_unlink_without_move (PHDR (&msd->root), &rbstack, 1); ADD_TO_FREE_LIST (msd, unlinked_node);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(unlinked_node->i.ind, T_DELETED);
5b15bb2001-12-10Martin Stjernholm  unlinked_node->i.prev = (struct msnode_ind *) prev; unlinked_node->i.ind.u.ptr = next; } else { unlinked_node = RBNODE (low_rb_unlink_with_move ( PHDR (&msd->root), &rbstack, 1,
f84a5a2015-04-26Henrik Grubbström (Grubba)  sizeof (struct msnode_ind)));
b3c3492001-12-10Martin Stjernholm  CLEAR_DELETED_ON_FREE_LIST (msd);
5b15bb2001-12-10Martin Stjernholm  ADD_TO_FREE_LIST (msd, unlinked_node);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(unlinked_node->i.ind, PIKE_T_UNKNOWN);
5b15bb2001-12-10Martin Stjernholm  unlinked_node->i.prev = NULL; msd->size--; } *track = rbstack; } #define GC_RECURSE_MSD_IN_USE(MSD, RECURSE_FN, IND_TYPES, VAL_TYPES) do { \ union msnode *node = low_multiset_first (MSD); \ IND_TYPES = msd->ind_types; \ if (node) { \ struct svalue ind; \ \
f84a5a2015-04-26Henrik Grubbström (Grubba)  do { \ low_use_multiset_index (node, ind); \ if (!IS_DESTRUCTED (&ind) && RECURSE_FN (&ind, 1)) { \ DO_IF_DEBUG (Pike_fatal ("Didn't expect an svalue zapping now.\n")); \ } \ } while ((node = low_multiset_next (node))); \
5b15bb2001-12-10Martin Stjernholm  } \ } while (0) /* This macro assumes that the msd isn't "in use", i.e. there are no * external references directly to it. In that case we can zap svalues * in it even if the mapping_data block is shared. */ #define GC_RECURSE(MSD, GOT_NODE_REFS, REC_NODE_I, REC_NODE_IV, TYPE, \ IND_TYPES, VAL_TYPES) do { \ struct rb_node_hdr *node = HDR (MSD->root); \ if (node) { \ struct svalue ind; \ int remove; \ RBSTACK_INIT (rbstack); \ LOW_RB_TRACK_FIRST (rbstack, node); \ \
f84a5a2015-04-26Henrik Grubbström (Grubba)  do { \ low_use_multiset_index (RBNODE (node), ind); \ REC_NODE_I ((&ind), \ remove, \ PIKE_CONCAT (TYPE, _svalues), \ PIKE_CONCAT (TYPE, _weak_svalues)); \ if (remove) { \ gc_unlink_msnode_shared (MSD, &rbstack, GOT_NODE_REFS); \ node = RBSTACK_PEEK (rbstack); \ } \ else { \ IND_TYPES |= 1 << TYPEOF(ind); \ LOW_RB_TRACK_NEXT (rbstack, node); \ } \ } while (node); \
5b15bb2001-12-10Martin Stjernholm  } \ } while (0) #define GC_REC_I_WEAK_NONE(IND, REMOVE, N_REC, W_REC) do { \ REMOVE = N_REC (IND, 1); \ } while (0) #define GC_REC_I_WEAK_IND(IND, REMOVE, N_REC, W_REC) do { \ REMOVE = W_REC (IND, 1); \ } while (0) #define GC_REC_IV_WEAK_NONE(IND, VAL, REMOVE, N_REC, W_REC, N_TST, W_TST) do { \ if ((REMOVE = N_REC (IND, 1))) \ gc_free_svalue (VAL); \ else \ N_REC (VAL, 1); \ } while (0) #define GC_REC_IV_WEAK_IND(IND, VAL, REMOVE, N_REC, W_REC, N_TST, W_TST) do { \ if ((REMOVE = W_REC (IND, 1))) \ gc_free_svalue (VAL); \ else \ N_REC (VAL, 1); \ } while (0)
b684132014-05-05Per Hedbor #if MULTISET_WEAK_VALUES
5b15bb2001-12-10Martin Stjernholm #define GC_REC_IV_WEAK_VAL(IND, VAL, REMOVE, N_REC, W_REC, N_TST, W_TST) do { \ if ((REMOVE = N_TST (IND))) /* Don't recurse now. */ \ gc_free_svalue (VAL); \ else if ((REMOVE = W_REC (VAL, 1))) \ gc_free_svalue (IND); \ else \ N_REC (IND, 1); /* Now we can recurse the index. */ \ } while (0) #define GC_REC_IV_WEAK_BOTH(IND, VAL, REMOVE, N_REC, W_REC, N_TST, W_TST) do { \ if ((REMOVE = W_TST (IND))) /* Don't recurse now. */ \ gc_free_svalue (VAL); \ else if ((REMOVE = W_REC (VAL, 1))) \ gc_free_svalue (IND); \ else \ W_REC (IND, 1); /* Now we can recurse the index. */ \ } while (0)
b684132014-05-05Per Hedbor #endif
5b15bb2001-12-10Martin Stjernholm  void gc_mark_multiset_as_referenced (struct multiset *l) {
613c342009-11-28Martin Stjernholm  if (gc_mark (l, T_MULTISET))
e1a35e2003-09-08Martin Stjernholm  GC_ENTER (l, T_MULTISET) { struct multiset_data *msd = l->msd;
5b15bb2001-12-10Martin Stjernholm 
e1a35e2003-09-08Martin Stjernholm  if (l == gc_mark_multiset_pos) gc_mark_multiset_pos = l->next; if (l == gc_internal_multiset) gc_internal_multiset = l->next; else { DOUBLEUNLINK (first_multiset, l); DOUBLELINK (first_multiset, l); /* Linked in first. */
5b15bb2001-12-10Martin Stjernholm  }
613c342009-11-28Martin Stjernholm  if (gc_mark (msd, T_MULTISET_DATA) && msd->root &&
5e83442008-05-11Martin Stjernholm  ((msd->ind_types | msd->val_types) & BIT_COMPLEX)) { struct marker *m = get_marker (msd); TYPE_FIELD ind_types = 0, val_types = 0;
5b15bb2001-12-10Martin Stjernholm 
abe64b2018-05-19Tobias S. Josefowitz  if (m->gc_flags & GC_MSD_GOT_EXT_REFS) {
5e83442008-05-11Martin Stjernholm  /* Must leave the multiset data untouched if there are direct * external refs to it. */ GC_RECURSE_MSD_IN_USE (msd, gc_mark_svalues, ind_types, val_types); gc_assert_checked_as_nonweak (msd); }
ad8d052008-05-02Martin Stjernholm 
5e83442008-05-11Martin Stjernholm  else { switch (msd->flags & MULTISET_WEAK) { case 0:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5e83442008-05-11Martin Stjernholm  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_NONE, gc_mark, ind_types, val_types); gc_assert_checked_as_nonweak (msd); break; case MULTISET_WEAK_INDICES:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5e83442008-05-11Martin Stjernholm  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_IND, gc_mark, ind_types, val_types); gc_assert_checked_as_weak (msd); break;
b684132014-05-05Per Hedbor #if MULTISET_WEAK_VALUES
5e83442008-05-11Martin Stjernholm  case MULTISET_WEAK_VALUES:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5e83442008-05-11Martin Stjernholm  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_VAL, gc_mark, ind_types, val_types); gc_assert_checked_as_weak (msd); break; default:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5e83442008-05-11Martin Stjernholm  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_BOTH, gc_mark, ind_types, val_types); gc_assert_checked_as_weak (msd);
b684132014-05-05Per Hedbor #endif
5e83442008-05-11Martin Stjernholm  break;
e1a35e2003-09-08Martin Stjernholm  }
5e83442008-05-11Martin Stjernholm  if (msd->refs == 1 && DO_SHRINK (msd, 0)) { /* Only shrink the multiset if it isn't shared, or else we * can end up with larger memory consumption since the * shrunk data blocks won't be shared. */ debug_malloc_touch (msd); l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0); debug_malloc_touch (l->msd); msd = l->msd;
e1a35e2003-09-08Martin Stjernholm  }
ad8d052008-05-02Martin Stjernholm  }
5e83442008-05-11Martin Stjernholm  msd->ind_types = ind_types;
e1a35e2003-09-08Martin Stjernholm  } } GC_LEAVE;
5b15bb2001-12-10Martin Stjernholm } void gc_mark_all_multisets (void) { gc_mark_multiset_pos = gc_internal_multiset; while (gc_mark_multiset_pos) { struct multiset *l = gc_mark_multiset_pos; gc_mark_multiset_pos = l->next; if (gc_is_referenced (l)) gc_mark_multiset_as_referenced (l); } } void real_gc_cycle_check_multiset (struct multiset *l, int weak) {
e1a35e2003-09-08Martin Stjernholm  GC_CYCLE_ENTER (l, T_MULTISET, weak) {
5b15bb2001-12-10Martin Stjernholm  struct multiset_data *msd = l->msd; if (msd->root && ((msd->ind_types | msd->val_types) & BIT_COMPLEX)) { struct marker *m = get_marker (msd); TYPE_FIELD ind_types = 0, val_types = 0;
abe64b2018-05-19Tobias S. Josefowitz  if (m->gc_flags & GC_MSD_GOT_EXT_REFS) {
5b15bb2001-12-10Martin Stjernholm  /* Must leave the multiset data untouched if there are direct * external refs to it. */ GC_RECURSE_MSD_IN_USE (msd, gc_cycle_check_svalues, ind_types, val_types); gc_assert_checked_as_nonweak (msd); } else { switch (msd->flags & MULTISET_WEAK) { case 0:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5b15bb2001-12-10Martin Stjernholm  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_NONE, gc_cycle_check, ind_types, val_types); gc_assert_checked_as_nonweak (msd); break; case MULTISET_WEAK_INDICES:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5b15bb2001-12-10Martin Stjernholm  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_IND, gc_cycle_check, ind_types, val_types); gc_assert_checked_as_weak (msd); break;
b684132014-05-05Per Hedbor #if MULTISET_WEAK_VALUES
5b15bb2001-12-10Martin Stjernholm  case MULTISET_WEAK_VALUES:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5b15bb2001-12-10Martin Stjernholm  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_VAL, gc_cycle_check, ind_types, val_types); gc_assert_checked_as_weak (msd); break; default:
abe64b2018-05-19Tobias S. Josefowitz  GC_RECURSE (msd, m->gc_flags & GC_MSD_GOT_NODE_REFS,
5b15bb2001-12-10Martin Stjernholm  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_BOTH, gc_cycle_check, ind_types, val_types); gc_assert_checked_as_weak (msd); break;
b684132014-05-05Per Hedbor #endif
5b15bb2001-12-10Martin Stjernholm  } if (msd->refs == 1 && DO_SHRINK (msd, 0)) { /* Only shrink the multiset if it isn't shared, or else we * can end up with larger memory consumption since the * shrunk data blocks won't be shared. */
28d6b72006-03-10Martin Stjernholm  debug_malloc_touch (msd);
5b15bb2001-12-10Martin Stjernholm  l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0);
28d6b72006-03-10Martin Stjernholm  debug_malloc_touch (l->msd);
5b15bb2001-12-10Martin Stjernholm  msd = l->msd; } } msd->ind_types = ind_types; } } GC_CYCLE_LEAVE; } void gc_cycle_check_all_multisets (void) { struct multiset *l; for (l = gc_internal_multiset; l; l = l->next) { real_gc_cycle_check_multiset (l, 0); gc_cycle_run_queue(); } } void gc_zap_ext_weak_refs_in_multisets (void) { gc_mark_multiset_pos = first_multiset; while (gc_mark_multiset_pos != gc_internal_multiset && gc_ext_weak_refs) { struct multiset *l = gc_mark_multiset_pos; gc_mark_multiset_pos = l->next; gc_mark_multiset_as_referenced (l); }
e1a35e2003-09-08Martin Stjernholm  gc_mark_discard_queue();
5b15bb2001-12-10Martin Stjernholm }
88cf4f2003-01-11Martin Stjernholm size_t gc_free_all_unreferenced_multisets (void)
5b15bb2001-12-10Martin Stjernholm { struct multiset *l, *next;
a1b3872003-01-11Martin Stjernholm  size_t unreferenced = 0;
5b15bb2001-12-10Martin Stjernholm  for (l = gc_internal_multiset; l; l = next) { if (gc_do_free (l)) { struct multiset_data *msd = l->msd; if (msd->root) { /* Replace the msd with an empty one to avoid recursion during free. */
f84a5a2015-04-26Henrik Grubbström (Grubba)  l->msd = &empty_ind_msd;
5b15bb2001-12-10Martin Stjernholm  add_ref (l->msd); if (!sub_ref (msd)) free_multiset_data (msd); } gc_free_extra_ref (l); SET_NEXT_AND_FREE (l, free_multiset); } else next = l->next;
a1b3872003-01-11Martin Stjernholm  unreferenced++;
5b15bb2001-12-10Martin Stjernholm  }
88cf4f2003-01-11Martin Stjernholm 
a1b3872003-01-11Martin Stjernholm  return unreferenced;
5b15bb2001-12-10Martin Stjernholm } void init_multiset() { #ifdef PIKE_DEBUG
f3e0a12006-01-14Martin Nilsson  /* This test is buggy in GCC 4.0.1, hence the volatile. */
88e81a2015-05-31Henrik Grubbström (Grubba)  union msnode test;
1abbfb2006-07-05Martin Stjernholm #define msnode_check(X) ((volatile union msnode *) (X))
5b15bb2001-12-10Martin Stjernholm  HDR (&test)->flags = 0;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(test.i.ind, (1 << 8) - 1, (1 << 16) - 1, refs, (INT32 *) (ptrdiff_t) -1);
5b15bb2001-12-10Martin Stjernholm  if (HDR (&test)->flags & (MULTISET_FLAG_MASK))
07794c2005-11-08Henrik Grubbström (Grubba)  Pike_fatal("The ind svalue overlays the flags field in an unexpected way.\n" "flags: 0x%08x, MULTISET_FLAG_MASK: 0x%08x\n", HDR(&test)->flags, MULTISET_FLAG_MASK);
5b15bb2001-12-10Martin Stjernholm  HDR (&test)->flags |= RB_FLAG_MASK;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(test.i.ind) & MULTISET_FLAG_MARKER)
819ef32006-01-14Martin Stjernholm  Pike_fatal("The ind svalue overlays the flags field in an unexpected way.\n"
9d3b722005-11-17Martin Nilsson  "type: 0x%08x, MULTISET_FLAG_MASK: 0x%08x\n"
07794c2005-11-08Henrik Grubbström (Grubba)  "RB_FLAG_MASK: 0x%08x, MULTISET_FLAG_MARKER: 0x%08x\n",
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(test.i.ind), MULTISET_FLAG_MASK,
07794c2005-11-08Henrik Grubbström (Grubba)  RB_FLAG_MASK, MULTISET_FLAG_MARKER);
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL_TYPE(test.i.ind, TYPEOF(test.i.ind) | MULTISET_FLAG_MARKER); if ((TYPEOF(test.i.ind) & ~MULTISET_FLAG_MASK) != (1 << 8) - 1)
819ef32006-01-14Martin Stjernholm  Pike_fatal("The ind svalue overlays the flags field in an unexpected way.\n" "flags: 0x%08x, MULTISET_FLAG_MASK: 0x%08x\n"
07794c2005-11-08Henrik Grubbström (Grubba)  "RB_FLAG_MASK: 0x%08x, MULTISET_FLAG_MARKER: 0x%08x\n" "type: 0x%08x\n",
819ef32006-01-14Martin Stjernholm  HDR(&test)->flags, MULTISET_FLAG_MASK,
07794c2005-11-08Henrik Grubbström (Grubba)  RB_FLAG_MASK, MULTISET_FLAG_MARKER,
017b572011-10-28Henrik Grubbström (Grubba)  TYPEOF(test.i.ind));
1abbfb2006-07-05Martin Stjernholm #undef msnode_check
5b15bb2001-12-10Martin Stjernholm #endif
d18ea62010-07-19Martin Stjernholm  #ifdef DEBUG_MALLOC /* Make some statically allocated structs known to dmalloc. */ dmalloc_register (&empty_ind_msd, sizeof (empty_ind_msd), DMALLOC_LOCATION()); dmalloc_accept_leak (&empty_ind_msd); #endif
5b15bb2001-12-10Martin Stjernholm } /* Pike might exit without calling this. */ void exit_multiset() { } #if defined (PIKE_DEBUG) || defined (TEST_MULTISET)
180ad22008-07-22Martin Stjernholm PMOD_EXPORT union msnode *debug_check_msnode ( struct multiset *l, ptrdiff_t nodepos, int allow_deleted, char *file, int line)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd; union msnode *node;
b3c3492001-12-10Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  if (l->node_refs <= 0)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("%s:%d: Got a node reference to a multiset without any.\n",
180ad22008-07-22Martin Stjernholm  file, line);
5b15bb2001-12-10Martin Stjernholm  if (nodepos < 0 || nodepos >= msd->allocsize)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("%s:%d: Node offset %"PRINTPTRDIFFT"d "
180ad22008-07-22Martin Stjernholm  "outside storage for multiset (size %d).\n", file, line, nodepos, msd->allocsize);
b3c3492001-12-10Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  node = OFF2MSNODE (msd, nodepos);
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(node->i.ind)) {
b3c3492001-12-10Martin Stjernholm  case T_DELETED: if (!allow_deleted)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("%s:%d: Node at offset %"PRINTPTRDIFFT"d is deleted.\n",
180ad22008-07-22Martin Stjernholm  file, line, nodepos);
b3c3492001-12-10Martin Stjernholm  break; case PIKE_T_UNKNOWN:
5aad932002-08-15Marcus Comstedt  Pike_fatal ("%s:%d: Invalid node offset %"PRINTPTRDIFFT"d.\n",
180ad22008-07-22Martin Stjernholm  file, line, nodepos);
0f440a2003-04-26Martin Stjernholm #ifdef PIKE_DEBUG
b3c3492001-12-10Martin Stjernholm  default:
017b572011-10-28Henrik Grubbström (Grubba)  if (!(TYPEOF(node->i.ind) & MULTISET_FLAG_MARKER)) {
9013c82006-07-07Martin Stjernholm #ifdef DEBUG_MALLOC fprintf (stderr, "%s:%d: %s", file, line, msg_no_multiset_flag_marker); locate_references (l); #endif
5aad932002-08-15Marcus Comstedt  Pike_fatal ("%s:%d: %s", file, line, msg_no_multiset_flag_marker);
9013c82006-07-07Martin Stjernholm  }
0f440a2003-04-26Martin Stjernholm #endif
b3c3492001-12-10Martin Stjernholm  }
5b15bb2001-12-10Martin Stjernholm  return node; }
b3c3492001-12-10Martin Stjernholm #define EXP_NODE_ALLOC 1 #define EXP_NODE_DEL 2 #define EXP_NODE_FREE 4 static void check_low_msnode (struct multiset_data *msd, union msnode *node, int exp_type)
5b15bb2001-12-10Martin Stjernholm { if (node < msd->nodes ||
f84a5a2015-04-26Henrik Grubbström (Grubba)  node >= (INODE (NODE_AT (msd, msnode_ind, msd->allocsize))))
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Node outside storage for multiset.\n");
5b15bb2001-12-10Martin Stjernholm  if ((char *) node - (char *) msd->nodes !=
f84a5a2015-04-26Henrik Grubbström (Grubba)  ((&node->i - &msd->nodes->i) *
72ddd92003-07-01Martin Stjernholm  (ptrdiff_t) sizeof (struct msnode_ind)))
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Unaligned node in storage for multiset.\n");
b3c3492001-12-10Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(node->i.ind)) {
b3c3492001-12-10Martin Stjernholm  default:
5aad932002-08-15Marcus Comstedt  if (!(exp_type & EXP_NODE_ALLOC)) Pike_fatal ("Node is in use.\n");
b3c3492001-12-10Martin Stjernholm  break; case T_DELETED:
5aad932002-08-15Marcus Comstedt  if (!(exp_type & EXP_NODE_DEL)) Pike_fatal ("Node is deleted.\n");
b3c3492001-12-10Martin Stjernholm  break; case PIKE_T_UNKNOWN:
5aad932002-08-15Marcus Comstedt  if (!(exp_type & EXP_NODE_FREE)) Pike_fatal ("Node is free.\n");
b3c3492001-12-10Martin Stjernholm  break; }
5b15bb2001-12-10Martin Stjernholm }
fd04632002-11-23Martin Stjernholm static int inside_check_multiset = 0; /* The safe flag can be used to avoid the checks that might call pike * code or alter memory structures. */ void check_multiset (struct multiset *l, int safe)
5b15bb2001-12-10Martin Stjernholm { struct multiset_data *msd = l->msd;
f84a5a2015-04-26Henrik Grubbström (Grubba)  int alloc = 0;
5b15bb2001-12-10Martin Stjernholm 
fd04632002-11-23Martin Stjernholm  if (inside_check_multiset) return; inside_check_multiset = 1;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT) if (SUBTYPEOF(msd->cmp_less) != NUMBER_NUMBER ||
7d61ba2008-05-30Martin Stjernholm  msd->cmp_less.u.integer != 0) Pike_fatal ("Multiset cmp_less is a nonzero integer: " "subtype=%d, value=%"PRINTPIKEINT"d\n",
017b572011-10-28Henrik Grubbström (Grubba)  SUBTYPEOF(msd->cmp_less), msd->cmp_less.u.integer);
7d61ba2008-05-30Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  /* Check refs and multiset link list. */ if (l->refs <= 0)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset has incorrect refs %d.\n", l->refs);
5b15bb2001-12-10Martin Stjernholm  if (l->node_refs < 0)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset has incorrect node_refs %d.\n", l->node_refs);
5b15bb2001-12-10Martin Stjernholm  if (!msd)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset has no data block.\n");
5b15bb2001-12-10Martin Stjernholm  if (msd->refs <= 0)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data block has incorrect refs %d.\n", msd->refs);
5b15bb2001-12-10Martin Stjernholm  if (msd->noval_refs < 0)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data block has negative noval_refs %d.\n", msd->noval_refs);
5b15bb2001-12-10Martin Stjernholm  if (msd->noval_refs > msd->refs)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data block has more noval_refs %d than refs %d.\n",
5b15bb2001-12-10Martin Stjernholm  msd->noval_refs, msd->refs); if (l->next && l->next->prev != l)
5aad932002-08-15Marcus Comstedt  Pike_fatal("multiset->next->prev != multiset.\n");
5b15bb2001-12-10Martin Stjernholm  if (l->prev) { if (l->prev->next != l)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("multiset->prev->next != multiset.\n");
5b15bb2001-12-10Martin Stjernholm  } else if (first_multiset != l)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("multiset->prev == 0 but first_multiset != multiset.\n");
5b15bb2001-12-10Martin Stjernholm  /* Check all node pointers, the tree structure and the type hints. */ {
b3c3492001-12-10Martin Stjernholm  union msnode *node;
f84a5a2015-04-26Henrik Grubbström (Grubba)  TYPE_FIELD ind_types = 0, val_types = BIT_INT;
5b15bb2001-12-10Martin Stjernholm  if (msd->root) { int pos;
b3c3492001-12-10Martin Stjernholm  check_low_msnode (msd, msd->root, EXP_NODE_ALLOC); if (msd->free_list) check_low_msnode (msd, msd->free_list, EXP_NODE_DEL | EXP_NODE_FREE); for (pos = msd->allocsize; pos-- > 0;) {
f84a5a2015-04-26Henrik Grubbström (Grubba)  node = INODE (NODE_AT (msd, msnode_ind, pos));
b3c3492001-12-10Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  switch (TYPEOF(node->i.ind)) {
b3c3492001-12-10Martin Stjernholm  case T_DELETED: if (node->i.next) /* Don't check the type of the pointed to node; the free * list check below gives a better error for that. */ check_low_msnode (msd, INODE (node->i.next), EXP_NODE_ALLOC | EXP_NODE_DEL | EXP_NODE_FREE); if (DELETED_PREV (node)) check_low_msnode (msd, DELETED_PREV (node), EXP_NODE_ALLOC | EXP_NODE_DEL); if (DELETED_NEXT (node)) check_low_msnode (msd, DELETED_NEXT (node), EXP_NODE_ALLOC | EXP_NODE_DEL); break;
5b15bb2001-12-10Martin Stjernholm 
b3c3492001-12-10Martin Stjernholm  case PIKE_T_UNKNOWN: if (node->i.next) /* Don't check the type of the pointed to node; the free * list check below gives a better error for that. */ check_low_msnode (msd, INODE (node->i.next), EXP_NODE_ALLOC | EXP_NODE_DEL | EXP_NODE_FREE); if (node->i.prev)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Free node got garbage in prev pointer.\n");
b3c3492001-12-10Martin Stjernholm  break;
5b15bb2001-12-10Martin Stjernholm 
b3c3492001-12-10Martin Stjernholm  default: alloc++;
0f440a2003-04-26Martin Stjernholm #ifdef PIKE_DEBUG
017b572011-10-28Henrik Grubbström (Grubba)  if (!(TYPEOF(node->i.ind) & MULTISET_FLAG_MARKER))
5aad932002-08-15Marcus Comstedt  Pike_fatal (msg_no_multiset_flag_marker);
0f440a2003-04-26Martin Stjernholm #endif
017b572011-10-28Henrik Grubbström (Grubba)  ind_types |= 1 << (TYPEOF(node->i.ind) & ~MULTISET_FLAG_MASK);
b3c3492001-12-10Martin Stjernholm  if (node->i.prev) check_low_msnode (msd, INODE (node->i.prev), EXP_NODE_ALLOC); if (node->i.next) check_low_msnode (msd, INODE (node->i.next), EXP_NODE_ALLOC); } }
5b15bb2001-12-10Martin Stjernholm  #ifdef PIKE_DEBUG debug_check_rb_tree (HDR (msd->root), (dump_data_fn *) debug_dump_ind_data, msd); #endif } if (ind_types & ~msd->ind_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset indices type field lacked 0x%x.\n", ind_types & ~msd->ind_types);
5b15bb2001-12-10Martin Stjernholm  if (val_types & ~msd->val_types)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset values type field lacked 0x%x.\n", val_types & ~msd->val_types);
5b15bb2001-12-10Martin Stjernholm  } /* Check the free list. */ {
b3c3492001-12-10Martin Stjernholm  int deleted = 0, free = 0;
5b15bb2001-12-10Martin Stjernholm  union msnode *node;
b3c3492001-12-10Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  for (node = msd->free_list; node; node = NEXT_FREE (node)) {
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) == PIKE_T_UNKNOWN) break; if (TYPEOF(node->i.ind) != T_DELETED) Pike_fatal ("Multiset node in free list got invalid type %d.\n", TYPEOF(node->i.ind));
b3c3492001-12-10Martin Stjernholm  deleted++;
5b15bb2001-12-10Martin Stjernholm  } if (node) {
b3c3492001-12-10Martin Stjernholm  free++;
5b15bb2001-12-10Martin Stjernholm  for (node = NEXT_FREE (node); node; node = NEXT_FREE (node)) {
b3c3492001-12-10Martin Stjernholm  free++;
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) == T_DELETED)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data got deleted node after free node on free list.\n");
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(node->i.ind) != PIKE_T_UNKNOWN) Pike_fatal ("Multiset node in free list got invalid type %d.\n", TYPEOF(node->i.ind));
5b15bb2001-12-10Martin Stjernholm  } }
b3c3492001-12-10Martin Stjernholm  if (msd->size != alloc + deleted)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data got size %d but tree has %d nodes and %d are deleted.\n",
b3c3492001-12-10Martin Stjernholm  msd->size, alloc, deleted);
5b15bb2001-12-10Martin Stjernholm 
b3c3492001-12-10Martin Stjernholm  if (free != msd->allocsize - msd->size)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Multiset data should have %d free nodes but got %d on free list.\n",
b3c3492001-12-10Martin Stjernholm  msd->allocsize - msd->size, free);
5b15bb2001-12-10Martin Stjernholm  } /* Check the order. This can call pike code, so we need to be extra careful. */
fd04632002-11-23Martin Stjernholm  if (!safe && msd->root) {
5b15bb2001-12-10Martin Stjernholm  JMP_BUF recovery; add_msnode_ref (l); if (SETJMP (recovery)) call_handle_error(); else { /* msd duplicated to avoid SETJMP clobber (or at least silence * gcc warnings about it). */ struct multiset_data *msd = l->msd; union msnode *node, *next; struct svalue tmp1, tmp2; ptrdiff_t nextpos; node = low_multiset_first (msd); low_use_multiset_index (node, tmp1); #ifdef PIKE_DEBUG check_svalue (&tmp1); #endif
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT)
5b15bb2001-12-10Martin Stjernholm  for (; (next = low_multiset_next (node)); node = next) { int cmp_res; low_use_multiset_index (next, tmp2); if (!IS_DESTRUCTED (&tmp2)) { #ifdef PIKE_DEBUG check_svalue (&tmp2); #endif nextpos = MSNODE2OFF (msd, next);
b0d15b2002-09-01Martin Stjernholm  /* FIXME: Handle destructed index in node. */
5b15bb2001-12-10Martin Stjernholm  INTERNAL_CMP (low_use_multiset_index (node, tmp1), &tmp2, cmp_res);
5625622011-03-06Martin Stjernholm  if (cmp_res > 0 && cmp_res != CMPFUN_UNORDERED)
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Order failure in multiset data with internal order.\n");
5b15bb2001-12-10Martin Stjernholm  if (l->msd != msd) { msd = l->msd; next = OFF2MSNODE (msd, nextpos);
017b572011-10-28Henrik Grubbström (Grubba)  while (next && TYPEOF(next->i.ind) == T_DELETED)
5b15bb2001-12-10Martin Stjernholm  next = DELETED_PREV (next); if (!next) { next = low_multiset_first (msd); if (!next) goto order_check_done; } } } } else for (; (next = low_multiset_next (node)); node = next) { low_push_multiset_index (next);
19961b2017-04-08Martin Nilsson  if (IS_DESTRUCTED (Pike_sp - 1))
0f440a2003-04-26Martin Stjernholm  pop_stack(); else {
5b15bb2001-12-10Martin Stjernholm #ifdef PIKE_DEBUG
19961b2017-04-08Martin Nilsson  check_svalue (Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm #endif low_push_multiset_index (node);
b0d15b2002-09-01Martin Stjernholm  /* FIXME: Handle destructed index in node. */
5b15bb2001-12-10Martin Stjernholm  nextpos = MSNODE2OFF (msd, next); EXTERNAL_CMP (&msd->cmp_less);
19961b2017-04-08Martin Nilsson  if (!UNSAFE_IS_ZERO (Pike_sp - 1))
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Order failure in multiset data with external order.\n");
5b15bb2001-12-10Martin Stjernholm  pop_stack(); if (l->msd != msd) { msd = l->msd; next = OFF2MSNODE (msd, nextpos);
017b572011-10-28Henrik Grubbström (Grubba)  while (next && TYPEOF(next->i.ind) == T_DELETED)
5b15bb2001-12-10Martin Stjernholm  next = DELETED_PREV (next); if (!next) { next = low_multiset_first (msd); if (!next) goto order_check_done; } } } } order_check_done:
4d66082002-12-16Henrik Grubbström (Grubba)  ;
5b15bb2001-12-10Martin Stjernholm  } UNSETJMP (recovery); sub_msnode_ref (l); }
fd04632002-11-23Martin Stjernholm  inside_check_multiset = 0;
5b15bb2001-12-10Martin Stjernholm }
fd04632002-11-23Martin Stjernholm void check_all_multisets (int safe)
5b15bb2001-12-10Martin Stjernholm { struct multiset *l; for (l = first_multiset; l; l = l->next)
fd04632002-11-23Martin Stjernholm  check_multiset (l, safe);
5b15bb2001-12-10Martin Stjernholm } static void debug_dump_ind_data (struct msnode_ind *node, struct multiset_data *msd) { struct svalue tmp; print_svalue (stderr, low_use_multiset_index (INODE (node), tmp));
13d0782004-05-19Martin Stjernholm  fprintf (stderr, " (%p) [%"PRINTPTRDIFFT"d]", tmp.u.refs, MSNODE2OFF (msd, INODE (node)));
5b15bb2001-12-10Martin Stjernholm }
12e5ef2014-11-18Henrik Grubbström (Grubba) #ifndef PIKE_DEBUG
b684132014-05-05Per Hedbor static void simple_describe_multiset (struct multiset *l) {
e0cd662016-12-28Arne Goedeke  struct byte_buffer buf = BUFFER_INIT(); describe_multiset (&buf, l, NULL, 2); buffer_add_str(&buf "\n"); fputs(buffer_get_string(&buf), stderr); buffer_free (desc);
b684132014-05-05Per Hedbor }
12e5ef2014-11-18Henrik Grubbström (Grubba) #endif
b684132014-05-05Per Hedbor 
5b15bb2001-12-10Martin Stjernholm void debug_dump_multiset (struct multiset *l) { struct multiset_data *msd = l->msd; fprintf (stderr, "Refs=%d, node_refs=%d, next=%p, prev=%p\nmsd=%p", l->refs, l->node_refs, l->next, l->prev, msd); if ((ptrdiff_t) msd & 3) fputs (" (unaligned)\n", stderr); else { fprintf (stderr, ", refs=%d, noval_refs=%d, flags=0x%x, size=%d, allocsize=%d\n", msd->refs, msd->noval_refs, msd->flags, msd->size, msd->allocsize); if (msd == &empty_ind_msd) fputs ("msd is empty_ind_msd\n", stderr);
0f440a2003-04-26Martin Stjernholm #ifdef PIKE_DEBUG
5b15bb2001-12-10Martin Stjernholm  fputs ("Indices type field =", stderr); debug_dump_type_field (msd->ind_types); fputs ("\nValues type field =", stderr); debug_dump_type_field (msd->val_types);
0f440a2003-04-26Martin Stjernholm #endif
5b15bb2001-12-10Martin Stjernholm 
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(msd->cmp_less) == T_INT)
5b15bb2001-12-10Martin Stjernholm  fputs ("\nInternal compare function\n", stderr); else { fputs ("\nCompare function = ", stderr); print_svalue (stderr, &msd->cmp_less); fputc ('\n', stderr); } #ifdef PIKE_DEBUG debug_dump_rb_tree (HDR (msd->root), (dump_data_fn *) debug_dump_ind_data, msd); #else simple_describe_multiset (l); #endif
017b572011-10-28Henrik Grubbström (Grubba)  if (msd->free_list && TYPEOF(msd->free_list->i.ind) == T_DELETED) {
5b15bb2001-12-10Martin Stjernholm  union msnode *node = msd->free_list; fputs ("Deleted nodes:", stderr); do { if (node != msd->free_list) fputc (',', stderr); fprintf (stderr, " %p [%"PRINTPTRDIFFT"d]", node, MSNODE2OFF (msd, node));
017b572011-10-28Henrik Grubbström (Grubba)  } while ((node = NEXT_FREE (node)) && TYPEOF(node->i.ind) == T_DELETED);
9013c82006-07-07Martin Stjernholm  fputc ('\n', stderr);
5b15bb2001-12-10Martin Stjernholm  } } } static void debug_multiset_fatal (struct multiset *l, const char *fmt, ...) { struct multiset_data *msd = l->msd; va_list args; va_start (args, fmt);
6a1ed02014-09-03Martin Nilsson  (void) vfprintf (stderr, fmt, args);
5b15bb2001-12-10Martin Stjernholm  fprintf (stderr, "Dumping multiset @ %p: ", l); debug_dump_multiset (l); debug_fatal ("\r"); } #ifdef TEST_MULTISET #define TEST_FIND(fn, exp) do { \
19961b2017-04-08Martin Nilsson  node = PIKE_CONCAT (multiset_, fn) (l, Pike_sp - 1); \
5b15bb2001-12-10Martin Stjernholm  if (node < 0) \ multiset_fatal (l, #fn " failed to find %d (%d).\n", exp, i); \ if (access_msnode (l, node)->i.ind.u.integer != exp) \
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, #fn " failed to find %d - " \ "got %"PRINTPIKEINT"d instead (%d).\n", \
5b15bb2001-12-10Martin Stjernholm  exp, access_msnode (l, node)->i.ind.u.integer, i); \ sub_msnode_ref (l); \ } while (0) #define TEST_NOT_FIND(fn) do { \
19961b2017-04-08Martin Nilsson  node = PIKE_CONCAT (multiset_, fn) (l, Pike_sp - 1); \
5b15bb2001-12-10Martin Stjernholm  if (node >= 0) \
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, #fn " failed to not find %"PRINTPIKEINT"d - " \ "got %"PRINTPIKEINT"d (%d).\n", \
19961b2017-04-08Martin Nilsson  Pike_sp[-1].u.integer, \
5b15bb2001-12-10Martin Stjernholm  access_msnode (l, node)->i.ind.u.integer, i); \ } while (0) #define TEST_STEP_FIND(fn, dir, exp) do { \ add_msnode_ref (l); /* Cheating. */ \ node = PIKE_CONCAT (multiset_, dir) (l, node); \ if (node < 0) \ multiset_fatal (l, "Failed to step " #dir " to %d after " #fn \
9013c82006-07-07Martin Stjernholm  " of %"PRINTPIKEINT"d (%d).\n", \
19961b2017-04-08Martin Nilsson  exp, Pike_sp[-1].u.integer, i); \
5b15bb2001-12-10Martin Stjernholm  if (access_msnode (l, node)->i.ind.u.integer != exp) \ multiset_fatal (l, "Failed to step " #dir " to %d after " #fn \
9013c82006-07-07Martin Stjernholm  " of %"PRINTPIKEINT"d - " \ "got %"PRINTPIKEINT"d instead (%d).\n", \
19961b2017-04-08Martin Nilsson  exp, Pike_sp[-1].u.integer, \
5b15bb2001-12-10Martin Stjernholm  access_msnode (l, node)->i.ind.u.integer, i); \ sub_msnode_ref (l); \ } while (0) #define TEST_STEP_NOT_FIND(fn, dir) do { \ add_msnode_ref (l); /* Cheating. */ \ node = PIKE_CONCAT (multiset_, dir) (l, node); \ if (node >= 0) \ multiset_fatal (l, "Failed to step " #dir " to end after " #fn \
9013c82006-07-07Martin Stjernholm  " of %"PRINTPIKEINT"d - " \ "got %"PRINTPIKEINT"d (%d).\n", \
19961b2017-04-08Martin Nilsson  Pike_sp[-1].u.integer, \
5b15bb2001-12-10Martin Stjernholm  access_msnode (l, node)->i.ind.u.integer, i); \ sub_msnode_ref (l); \ } while (0) static int naive_test_equal (struct multiset *a, struct multiset *b) { union msnode *na, *nb; struct svalue sa, sb; na = low_multiset_first (a->msd); nb = low_multiset_first (b->msd); while (na && nb) { low_use_multiset_index (na, sa); low_use_multiset_index (nb, sb);
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(sa) != TYPEOF(sb) || sa.u.integer != sb.u.integer ||
f84a5a2015-04-26Henrik Grubbström (Grubba)  na->iv.val.u.integer != nb->iv.val.u.integer))) return 0;
5b15bb2001-12-10Martin Stjernholm  na = low_multiset_next (na); nb = low_multiset_next (nb); } return !(na || nb); } static void debug_merge_fatal (struct multiset *a, struct multiset *b, struct multiset *exp, struct multiset *got, const char *fmt, ...) { va_list args; va_start (args, fmt);
6a1ed02014-09-03Martin Nilsson  (void) vfprintf (stderr, fmt, args);
5b15bb2001-12-10Martin Stjernholm  fputs ("Dumping a: ", stderr); debug_dump_multiset (a); fputs ("Dumping b: ", stderr); debug_dump_multiset (b); fputs ("Dumping expected: ", stderr); debug_dump_multiset (exp); fputs ("Dumping got: ", stderr); debug_dump_multiset (got); debug_fatal ("\r"); }
90f9602003-01-08Henrik Grubbström (Grubba) #ifdef TEST_MULTISET_VERBOSE
a91d9a2016-01-11Martin Nilsson #define TM_VERBOSE(...) fprintf(stderr, __VA_ARGS__)
90f9602003-01-08Henrik Grubbström (Grubba) #else /* !TEST_MULTISET_VERBOSE */
a91d9a2016-01-11Martin Nilsson #define TM_VERBOSE(...)
90f9602003-01-08Henrik Grubbström (Grubba) #endif /* TEST_MULTISET_VERBOSE */
5b15bb2001-12-10Martin Stjernholm void test_multiset (void) { int pass, i, j, v, vv, old_d_flag = d_flag;
19961b2017-04-08Martin Nilsson  struct svalue *less_efun, *greater_efun, tmp, *orig_sp = Pike_sp;
5b15bb2001-12-10Martin Stjernholm  struct array *arr; struct multiset *l, *l2; ptrdiff_t node; d_flag = 3; push_svalue (simple_mapping_string_lookup (get_builtin_constants(), "`<"));
19961b2017-04-08Martin Nilsson  less_efun = Pike_sp - 1;
5b15bb2001-12-10Martin Stjernholm  push_svalue (simple_mapping_string_lookup (get_builtin_constants(), "`>"));
19961b2017-04-08Martin Nilsson  greater_efun = Pike_sp - 1;
5b15bb2001-12-10Martin Stjernholm  for (pass = 0; pass < 2; pass++) { push_int (1); push_int (1); push_int (2); push_int (4); push_int (5); push_int (5); push_int (7); push_int (8); push_int (11); push_int (14); push_int (15); push_int (15); f_aggregate (12); for (i = 1*2*3*4*5*6*7*8*9; i > 0; i--) { if (!(i % 1000)) fprintf (stderr, "ind %s %d \r", pass ? "cmp_less" : "internal", i);
13670c2015-05-25Martin Nilsson 
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("pass:%d, i:%d\n", pass, i);
5b15bb2001-12-10Martin Stjernholm  l = allocate_multiset (0, 0, pass ? less_efun : NULL); stack_dup(); push_int (i); f_permute (2);
19961b2017-04-08Martin Nilsson  arr = Pike_sp[-1].u.array;
5b15bb2001-12-10Martin Stjernholm 
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("insert: ");
5b15bb2001-12-10Martin Stjernholm  for (j = 0; j < 12; j++) {
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("arr[%d]=%d ", j, arr->item[j].u.integer);
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_insert_2 (l, &arr->item[j]);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  } if (multiset_sizeof (l) != 9) multiset_fatal (l, "Size is wrong: %d (%d)\n", multiset_sizeof (l), i);
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("\nfind 5 ");
5b15bb2001-12-10Martin Stjernholm  push_int (5); TEST_FIND (find_eq, 5); TEST_FIND (find_lt, 4); TEST_FIND (find_gt, 7); TEST_FIND (find_le, 5); TEST_FIND (find_ge, 5); pop_stack();
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("6 ");
5b15bb2001-12-10Martin Stjernholm  push_int (6); TEST_NOT_FIND (find_eq); TEST_FIND (find_lt, 5); TEST_FIND (find_gt, 7); TEST_FIND (find_le, 5); TEST_FIND (find_ge, 7); pop_stack();
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("0 ");
5b15bb2001-12-10Martin Stjernholm  push_int (0); TEST_NOT_FIND (find_eq); TEST_NOT_FIND (find_lt); TEST_FIND (find_gt, 1); TEST_NOT_FIND (find_le); TEST_FIND (find_ge, 1); pop_stack();
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("1 ");
5b15bb2001-12-10Martin Stjernholm  push_int (1); TEST_FIND (find_eq, 1); TEST_NOT_FIND (find_lt); TEST_FIND (find_gt, 2); TEST_FIND (find_le, 1); TEST_FIND (find_ge, 1); pop_stack();
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("15 ");
5b15bb2001-12-10Martin Stjernholm  push_int (15); TEST_FIND (find_eq, 15); TEST_FIND (find_lt, 14); TEST_NOT_FIND (find_gt); TEST_FIND (find_le, 15); TEST_FIND (find_ge, 15); pop_stack();
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("17\n");
5b15bb2001-12-10Martin Stjernholm  push_int (17); TEST_NOT_FIND (find_eq); TEST_FIND (find_lt, 15); TEST_NOT_FIND (find_gt); TEST_FIND (find_le, 15); TEST_NOT_FIND (find_ge); pop_stack(); l2 = l;
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("delete: ");
5b15bb2001-12-10Martin Stjernholm  for (j = 0, v = 0; j < 12; j++) {
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("arr[%d]=%d ", j, arr->item[j].u.integer);
5b15bb2001-12-10Martin Stjernholm  v += !!multiset_delete_2 (l2, &arr->item[j], NULL); if (multiset_find_eq (l2, &arr->item[j]) >= 0)
9013c82006-07-07Martin Stjernholm  multiset_fatal (l2, "Entry %"PRINTPIKEINT"d not deleted (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i);
fd04632002-11-23Martin Stjernholm  check_multiset (l2, 0);
5b15bb2001-12-10Martin Stjernholm  } if (v != 9 || l2->msd->root) multiset_fatal (l2, "Wrong number of entries deleted: %d (%d)\n", v, i);
a91d9a2016-01-11Martin Nilsson  TM_VERBOSE("\n");
5b15bb2001-12-10Martin Stjernholm  free_multiset (l); pop_stack(); } pop_stack(); } for (pass = 0; pass < 2; pass++) { push_int (1); push_int (1); push_int (4); push_int (5); push_int (5); push_int (7); push_int (15); push_int (15); f_aggregate (8); for (i = 1*2*3*4*5*6*7*8; i > 0; i--) { if (!(i % 1000)) fprintf (stderr, "indval %s %d \r", pass ? "cmp_less" : "internal", i); stack_dup(); push_int (i); f_permute (2);
19961b2017-04-08Martin Nilsson  arr = Pike_sp[-1].u.array;
5b15bb2001-12-10Martin Stjernholm  { ptrdiff_t nodes[8];
f84a5a2015-04-26Henrik Grubbström (Grubba)  l = allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  push_int (17); for (j = 0; j < 8; j++) { int node_ref = (node = multiset_last (l)) >= 0; for (; node >= 0; node = multiset_prev (l, node)) { if (get_multiset_value (l, node)->u.integer <= arr->item[j].u.integer) break; }
19961b2017-04-08Martin Nilsson  nodes[j] = multiset_add_after (l, node, Pike_sp - 1, &arr->item[j]);
5b15bb2001-12-10Martin Stjernholm  if (node_ref) sub_msnode_ref (l); if (nodes[j] < 0) { if (node < 0)
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, "Failed to add " "%"PRINTPIKEINT"d:%"PRINTPIKEINT"d first: " "%"PRINTPTRDIFFT"d\n",
19961b2017-04-08Martin Nilsson  Pike_sp[-1].u.integer, arr->item[j].u.integer, nodes[j]);
5b15bb2001-12-10Martin Stjernholm  else
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, "Failed to add " "%"PRINTPIKEINT"d:%"PRINTPIKEINT"d after " "%"PRINTPIKEINT"d:%"PRINTPIKEINT"d: " "%"PRINTPTRDIFFT"d\n",
19961b2017-04-08Martin Nilsson  Pike_sp[-1].u.integer, arr->item[j].u.integer,
5b15bb2001-12-10Martin Stjernholm  use_multiset_index (l, node, tmp)->u.integer,
0f440a2003-04-26Martin Stjernholm  get_multiset_value (l, node)->u.integer, nodes[j]);
5b15bb2001-12-10Martin Stjernholm  } add_msnode_ref (l);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  } if (j != 8) multiset_fatal (l, "Size is wrong: %d (%d)\n", j, i); add_msnode_ref (l); for (j = 0; j < 8; j++) { multiset_delete_node (l, nodes[j]);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  } sub_msnode_ref (l); if (multiset_sizeof (l)) multiset_fatal (l, "Whole tree not deleted (%d)\n", i); free_multiset (l); pop_stack(); }
f84a5a2015-04-26Henrik Grubbström (Grubba)  l = allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  for (j = 0; j < 8; j++) { push_int (arr->item[j].u.integer * 8 + j);
19961b2017-04-08Martin Nilsson  multiset_add (l, &arr->item[j], Pike_sp - 1);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); } for (j = 0, v = 0, node = multiset_first (l); node >= 0; node = multiset_next (l, node), j++) { push_multiset_index (l, node); if (v >= get_multiset_value (l, node)->u.integer) multiset_fatal (l, "Failed to sort values (%d).\n", i); v = get_multiset_value (l, node)->u.integer; pop_stack(); } if (j != 8 || multiset_sizeof (l) != j) multiset_fatal (l, "Size is wrong: %d (%d)\n", j, i); sub_msnode_ref (l); push_int (5); TEST_FIND (find_eq, 5); TEST_STEP_FIND (find_eq, next, 7); TEST_FIND (find_lt, 4); TEST_FIND (find_gt, 7); TEST_FIND (find_le, 5); TEST_STEP_FIND (find_le, next, 7); TEST_FIND (find_ge, 5); TEST_STEP_FIND (find_ge, prev, 4); pop_stack(); push_int (6); TEST_NOT_FIND (find_eq); TEST_FIND (find_lt, 5); TEST_FIND (find_gt, 7); TEST_FIND (find_le, 5); TEST_STEP_FIND (find_le, next, 7); TEST_FIND (find_ge, 7); TEST_STEP_FIND (find_ge, prev, 5); pop_stack(); push_int (0); TEST_NOT_FIND (find_eq); TEST_NOT_FIND (find_lt); TEST_FIND (find_gt, 1); TEST_STEP_NOT_FIND (find_gt, prev); TEST_NOT_FIND (find_le); TEST_FIND (find_ge, 1); TEST_STEP_FIND (find_ge, next, 1); pop_stack(); push_int (1); TEST_FIND (find_eq, 1); TEST_STEP_FIND (find_eq, next, 4); TEST_NOT_FIND (find_lt); TEST_FIND (find_gt, 4); TEST_FIND (find_le, 1); TEST_STEP_FIND (find_le, next, 4); TEST_FIND (find_ge, 1); TEST_STEP_NOT_FIND (find_ge, prev); pop_stack(); push_int (15); TEST_FIND (find_eq, 15); TEST_STEP_NOT_FIND (find_eq, next); TEST_FIND (find_lt, 7); TEST_NOT_FIND (find_gt); TEST_FIND (find_le, 15); TEST_STEP_NOT_FIND (find_le, next); TEST_FIND (find_ge, 15); TEST_STEP_FIND (find_ge, prev, 7); pop_stack(); push_int (17); TEST_NOT_FIND (find_eq); TEST_FIND (find_lt, 15); TEST_STEP_NOT_FIND (find_lt, next); TEST_NOT_FIND (find_gt); TEST_FIND (find_le, 15); TEST_STEP_FIND (find_le, prev, 15); TEST_NOT_FIND (find_ge); pop_stack(); l2 = copy_multiset (l);
fd04632002-11-23Martin Stjernholm  check_multiset (l2, 0);
5b15bb2001-12-10Martin Stjernholm  if (!naive_test_equal (l, l2)) multiset_fatal (l2, "Copy not equal to original (%d).\n", i); push_int (-1); for (j = 0; j < 8; j++) {
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_insert_2 (l2, &arr->item[j]);
5b15bb2001-12-10Martin Stjernholm  if (multiset_sizeof (l2) != multiset_sizeof (l))
9013c82006-07-07Martin Stjernholm  multiset_fatal (l2, "Duplicate entry " "%"PRINTPIKEINT"d inserted (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); if (get_multiset_value ( l2, multiset_find_eq (l2, &arr->item[j]))->u.integer == -1)
9013c82006-07-07Martin Stjernholm  multiset_fatal (l2, "Insert replaced last entry " "%"PRINTPIKEINT"d (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); sub_msnode_ref (l2); } for (j = 0; j < 8; j++) {
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_insert_2 (l2, &arr->item[j]);
5b15bb2001-12-10Martin Stjernholm  if (multiset_sizeof (l2) != multiset_sizeof (l))
9013c82006-07-07Martin Stjernholm  multiset_fatal (l2, "Duplicate entry " "%"PRINTPIKEINT"d inserted (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); if (get_multiset_value ( l2, multiset_find_eq (l2, &arr->item[j]))->u.integer != -1)
9013c82006-07-07Martin Stjernholm  multiset_fatal (l2, "Insert didn't replace last entry " "%"PRINTPIKEINT"d (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); sub_msnode_ref (l2); } pop_stack(); for (v = 0; multiset_sizeof (l2); v++) { add_msnode_ref (l2); multiset_delete_node (l2, MSNODE2OFF (l2->msd, l2->msd->root));
fd04632002-11-23Martin Stjernholm  check_multiset (l2, 0);
5b15bb2001-12-10Martin Stjernholm  } if (v != 8) multiset_fatal (l2, "Wrong number of entries deleted: %d (%d)\n", v, i); free_multiset (l2); for (j = 0, v = 0; j < 8; j++) { if (!multiset_delete_2 (l, &arr->item[j], &tmp))
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, "Entry %"PRINTPIKEINT"d not deleted (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); if ((node = multiset_find_eq (l, &arr->item[j])) >= 0) { if (get_multiset_value (l, node)->u.integer >= tmp.u.integer)
9013c82006-07-07Martin Stjernholm  multiset_fatal (l, "Last entry %"PRINTPIKEINT"d not deleted (%d).\n",
5b15bb2001-12-10Martin Stjernholm  arr->item[j].u.integer, i); sub_msnode_ref (l); } free_svalue (&tmp);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  } free_multiset (l); pop_stack(); } pop_stack(); } for (pass = 0; pass < 2; pass++) { int max = 1000000; l = allocate_multiset (0, 0, pass ? less_efun : NULL);
0d659f2016-03-15Martin Nilsson  srand (0);
5b15bb2001-12-10Martin Stjernholm #ifdef RB_STATS reset_rb_stats(); #endif
4819722009-04-06Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  for (i = max, v = 0; i > 0; i--) { if (!(i % 10000)) fprintf (stderr, "grow %s %d, %d duplicates \r", pass ? "cmp_less" : "internal", i, v);
0d659f2016-03-15Martin Nilsson  push_int (rand());
19961b2017-04-08Martin Nilsson  if (multiset_find_eq (l, Pike_sp - 1) >= 0) {
5b15bb2001-12-10Martin Stjernholm  v++; sub_msnode_ref (l); }
19961b2017-04-08Martin Nilsson  multiset_add (l, Pike_sp - 1, NULL);
4819722009-04-06Martin Stjernholm  { struct multiset_data *msd = l->msd; RBSTACK_INIT (rbstack_find); add_ref (msd);
19961b2017-04-08Martin Nilsson  if (low_multiset_track_eq (msd, Pike_sp - 1, &rbstack_find) != FIND_EQUAL)
4819722009-04-06Martin Stjernholm  fprintf (stderr, "Round %d: Didn't find node after add.\n", i); else { RBSTACK_INIT (rbstack_build); low_rb_build_stack (HDR (msd->root), RBSTACK_PEEK (rbstack_find), &rbstack_build); while (1) { struct rb_node_hdr *f, *b; RBSTACK_POP (rbstack_find, f); RBSTACK_POP (rbstack_build, b); if (f != b) { fprintf (stderr, "Round %d: find and build stacks differ.\n", i); break; } if (!f) break; } RBSTACK_FREE (rbstack_build); } RBSTACK_FREE (rbstack_find); sub_extra_ref (msd); }
5b15bb2001-12-10Martin Stjernholm  pop_stack(); }
4819722009-04-06Martin Stjernholm 
a28f532002-12-22Martin Stjernholm  if (v != 114) fprintf (stderr, "Got %d duplicates but expected 114 - " "the pseudorandom sequence isn't as expected\n", v);
4819722009-04-06Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm #ifdef RB_STATS fputc ('\n', stderr), print_rb_stats (1); #endif
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
0d659f2016-03-15Martin Nilsson  srand (0);
4819722009-04-06Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm  for (i = max; i > 0; i--) { if (!(i % 10000)) fprintf (stderr, "shrink %s %d \r", pass ? "cmp_less" : "internal", i);
0d659f2016-03-15Martin Nilsson  push_int (rand());
19961b2017-04-08Martin Nilsson  if (!multiset_delete (l, Pike_sp - 1))
5aad932002-08-15Marcus Comstedt  Pike_fatal ("Pseudo-random sequence didn't repeat.\n");
5b15bb2001-12-10Martin Stjernholm  pop_stack(); }
4819722009-04-06Martin Stjernholm 
5b15bb2001-12-10Martin Stjernholm #ifdef RB_STATS fputc ('\n', stderr), print_rb_stats (1); #endif if (multiset_sizeof (l)) multiset_fatal (l, "Multiset not empty.\n"); free_multiset (l); } if (1) { int max = 400; struct array *arr = allocate_array_no_init (0, max); struct svalue *sval;
0d659f2016-03-15Martin Nilsson  srand (0);
5b15bb2001-12-10Martin Stjernholm  for (i = j = 0; i < max; i++) { if (!(i % 10)) fprintf (stderr, "maketree %d \r", max * 10 - arr->size);
f84a5a2015-04-26Henrik Grubbström (Grubba)  l = mkmultiset_2 (arr, i & 1 ? less_efun : NULL);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  multiset_set_cmp_less (l, i & 4 ? less_efun : NULL);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
f84a5a2015-04-26Henrik Grubbström (Grubba)  multiset_set_flags (l, 0);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  multiset_set_cmp_less (l, greater_efun);
fd04632002-11-23Martin Stjernholm  check_multiset (l, 0);
5b15bb2001-12-10Martin Stjernholm  if ((node = multiset_first (l)) >= 0) {
0d659f2016-03-15Martin Nilsson  int pos = 0, try_get = (rand() & INT_MAX) % arr->size;
5b15bb2001-12-10Martin Stjernholm  for (; node >= 0; node = multiset_next (l, node), pos++) if (pos == try_get) { if ((v = use_multiset_index ( l, multiset_get_nth (l, try_get), tmp)->u.integer) != arr->size - try_get - 1) multiset_fatal (l, "Element @ %d is %d, but %d was expected (%d).\n", try_get, v, arr->size - try_get - 1, i); sub_msnode_ref (l); if ((v = get_multiset_value (l, node)->u.integer) != (vv = ((i & (8|2)) == (8|2) ? arr->size - try_get - 1 : 1))) multiset_fatal (l, "Element @ %d got value %d, but %d was expected (%d).\n", try_get, v, vv, i); break; } sub_msnode_ref (l); } free_multiset (l); arr = resize_array (arr, j + 10); for (; j < arr->size; j++) ITEM (arr)[j].u.integer = j; } free_array (arr); } for (pass = 0; pass < 1; pass++) { struct multiset *a =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *b =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *and =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *or =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *add =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *sub =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  struct multiset *xor =
f84a5a2015-04-26Henrik Grubbström (Grubba)  allocate_multiset (0, 0, pass ? less_efun : NULL);
5b15bb2001-12-10Martin Stjernholm  int action = 0;
0d659f2016-03-15Martin Nilsson  srand (0);
5b15bb2001-12-10Martin Stjernholm  for (i = 5000; i >= 0; i--, action = action % 6 + 1) {
0d659f2016-03-15Martin Nilsson  int nr = rand() & INT_MAX; /* Assumes we keep within one period. */
5b15bb2001-12-10Martin Stjernholm  if (!(i % 100)) fprintf (stderr, "merge %d \r", i); switch (action) { case 1: /* Unique index added to a only. */ push_int (nr); push_int (1);
19961b2017-04-08Martin Nilsson  multiset_add (a, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1); multiset_add (sub, Pike_sp - 2, Pike_sp - 1); multiset_add (xor, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  goto add_unique; case 2: /* Unique index added to b only. */ push_int (nr); push_int (2);
19961b2017-04-08Martin Nilsson  multiset_add (b, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1); multiset_add (xor, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  goto add_unique; case 3: /* Unique index added to a and b. */ push_int (nr); push_int (1);
19961b2017-04-08Martin Nilsson  multiset_add (a, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); push_int (2);
19961b2017-04-08Martin Nilsson  multiset_add (b, Pike_sp - 2, Pike_sp - 1); multiset_add (and, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  add_unique:
19961b2017-04-08Martin Nilsson  if (multiset_lookup (or, Pike_sp - 2))
5b15bb2001-12-10Martin Stjernholm  multiset_fatal (or, "Duplicate index %d not expected here.\n", nr);
19961b2017-04-08Martin Nilsson  multiset_insert_2 (or, Pike_sp - 2);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); pop_stack(); break; case 4: /* Duplicate index added to a only. */ nr = low_use_multiset_index ( low_multiset_get_nth ( sub->msd, nr % multiset_sizeof (sub)), tmp)->u.integer; push_int (nr); push_int (1);
19961b2017-04-08Martin Nilsson  multiset_add (a, Pike_sp - 2, Pike_sp - 1); multiset_add (or, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1); multiset_add (sub, Pike_sp - 2, Pike_sp - 1); multiset_add (xor, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); pop_stack(); break; case 5: /* Duplicate index added to b only. */ nr = low_use_multiset_index ( low_multiset_get_nth ( b->msd, nr % multiset_sizeof (b)), tmp)->u.integer; push_int (nr); push_int (2);
19961b2017-04-08Martin Nilsson  multiset_add (b, Pike_sp - 2, Pike_sp - 1); multiset_add (or, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1); multiset_add (xor, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); pop_stack(); break; case 6: /* Duplicate index added to a and b. */ nr = low_use_multiset_index ( low_multiset_get_nth ( b->msd, nr % multiset_sizeof (b)), tmp)->u.integer; push_int (nr); push_int (1);
19961b2017-04-08Martin Nilsson  multiset_add (a, Pike_sp - 2, Pike_sp - 1); node = multiset_find_lt (add, Pike_sp - 2); if ((nr = multiset_add_after (add, node, Pike_sp - 2, Pike_sp - 1)) < 0)
9013c82006-07-07Martin Stjernholm  multiset_fatal (add, "Failed to add " "%"PRINTPIKEINT"d:1 after " "%"PRINTPTRDIFFT"d: %d.\n",
19961b2017-04-08Martin Nilsson  Pike_sp[-2].u.integer, node, nr);
5b15bb2001-12-10Martin Stjernholm  if (node >= 0) sub_msnode_ref (add); pop_stack(); push_int (2);
19961b2017-04-08Martin Nilsson  multiset_add (b, Pike_sp - 2, Pike_sp - 1); multiset_add (and, Pike_sp - 2, Pike_sp - 1); multiset_add (or, Pike_sp - 2, Pike_sp - 1); multiset_add (add, Pike_sp - 2, Pike_sp - 1);
5b15bb2001-12-10Martin Stjernholm  pop_stack(); pop_stack(); break; } if (i % 10) continue; l = merge_multisets (a, b, PIKE_ARRAY_OP_AND); if (!naive_test_equal (and, l)) debug_merge_fatal (a, b, and, l, "Invalid 'and' merge (%d).\n", i); free_multiset (l); l = copy_multiset (a); merge_multisets (l, b, PIKE_ARRAY_OP_AND | PIKE_MERGE_DESTR_A); if (!naive_test_equal (and, l)) debug_merge_fatal (a, b, and, l, "Invalid destructive 'and' merge (%d).\n", i); free_multiset (l); l = merge_multisets (a, b, PIKE_ARRAY_OP_OR); if (!naive_test_equal (or, l)) debug_merge_fatal (a, b, or, l, "Invalid 'or' merge (%d).\n", i); free_multiset (l); l = copy_multiset (a); merge_multisets (l, b, PIKE_ARRAY_OP_OR | PIKE_MERGE_DESTR_A); if (!naive_test_equal (or, l)) debug_merge_fatal (a, b, or, l, "Invalid destructive 'or' merge (%d).\n", i); free_multiset (l); l = merge_multisets (a, b, PIKE_ARRAY_OP_ADD); if (!naive_test_equal (add, l)) debug_merge_fatal (a, b, add, l, "Invalid 'add' merge (%d).\n", i); free_multiset (l); l = copy_multiset (a); merge_multisets (l, b, PIKE_ARRAY_OP_ADD | PIKE_MERGE_DESTR_A); if (!naive_test_equal (add, l)) debug_merge_fatal (a, b, add, l, "Invalid destructive 'add' merge (%d).\n", i); free_multiset (l); l = merge_multisets (a, b, PIKE_ARRAY_OP_SUB); if (!naive_test_equal (sub, l)) debug_merge_fatal (a, b, sub, l, "Invalid 'sub' merge (%d).\n", i); free_multiset (l); l = copy_multiset (a); merge_multisets (l, b, PIKE_ARRAY_OP_SUB | PIKE_MERGE_DESTR_A); if (!naive_test_equal (sub, l)) debug_merge_fatal (a, b, sub, l, "Invalid destructive 'sub' merge (%d).\n", i); free_multiset (l); l = merge_multisets (a, b, PIKE_ARRAY_OP_XOR); if (!naive_test_equal (xor, l)) debug_merge_fatal (a, b, xor, l, "Invalid 'xor' merge (%d).\n", i); free_multiset (l); l = copy_multiset (a); merge_multisets (l, b, PIKE_ARRAY_OP_XOR | PIKE_MERGE_DESTR_A); if (!naive_test_equal (xor, l)) debug_merge_fatal (a, b, xor, l, "Invalid destructive 'xor' merge (%d).\n", i); free_multiset (l);
fd04632002-11-23Martin Stjernholm  check_multiset (a, 0);
5b15bb2001-12-10Martin Stjernholm  } free_multiset (a); free_multiset (b); free_multiset (and); free_multiset (or); free_multiset (add); free_multiset (sub); free_multiset (xor); }
0f440a2003-04-26Martin Stjernholm  pop_2_elems();
19961b2017-04-08Martin Nilsson  if (orig_sp != Pike_sp) Pike_fatal ("Stack wrong: %"PRINTPTRDIFFT"d extra elements.\n", Pike_sp - orig_sp);
5b15bb2001-12-10Martin Stjernholm  fprintf (stderr, " \r"); d_flag = old_d_flag; } #endif /* TEST_MULTISET */ #endif /* PIKE_DEBUG || TEST_MULTISET */