| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | package structr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"unsafe" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	"codeberg.org/gruf/go-byteutil" | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IndexConfig defines config variables | 
					
						
							|  |  |  | // for initializing a struct index. | 
					
						
							|  |  |  | type IndexConfig struct { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fields should contain a comma-separated | 
					
						
							|  |  |  | 	// list of struct fields used when generating | 
					
						
							|  |  |  | 	// keys for this index. Nested fields should | 
					
						
							|  |  |  | 	// be specified using periods. An example: | 
					
						
							|  |  |  | 	// "Username,Favorites.Color" | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 	// Note that nested fields where the nested | 
					
						
							|  |  |  | 	// struct field is a ptr are supported, but | 
					
						
							|  |  |  | 	// nil ptr values in nesting will result in | 
					
						
							|  |  |  | 	// that particular value NOT being indexed. | 
					
						
							|  |  |  | 	// e.g. with "Favorites.Color" if *Favorites | 
					
						
							|  |  |  | 	// is nil then it will not be indexed. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Field types supported include any of those | 
					
						
							|  |  |  | 	// supported by the `go-mangler` library. | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	Fields string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Multiple indicates whether to accept multiple | 
					
						
							|  |  |  | 	// possible values for any single index key. The | 
					
						
							|  |  |  | 	// default behaviour is to only accept one value | 
					
						
							|  |  |  | 	// and overwrite existing on any write operation. | 
					
						
							|  |  |  | 	Multiple bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// AllowZero indicates whether to accept zero | 
					
						
							|  |  |  | 	// value fields in index keys. i.e. whether to | 
					
						
							|  |  |  | 	// index structs for this set of field values | 
					
						
							|  |  |  | 	// IF any one of those field values is the zero | 
					
						
							|  |  |  | 	// value for that type. The default behaviour | 
					
						
							|  |  |  | 	// is to skip indexing structs for this lookup | 
					
						
							|  |  |  | 	// when any of the indexing fields are zero. | 
					
						
							|  |  |  | 	AllowZero bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Index is an exposed Cache internal model, used to | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | // extract struct keys, generate hash checksums for them | 
					
						
							|  |  |  | // and store struct results by the init defined config. | 
					
						
							|  |  |  | // This model is exposed to provide faster lookups in the | 
					
						
							|  |  |  | // case that you would like to manually provide the used | 
					
						
							|  |  |  | // index via the Cache.___By() series of functions, or | 
					
						
							|  |  |  | // access the underlying index key generator. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | type Index struct { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ptr is a pointer to | 
					
						
							|  |  |  | 	// the source Cache/Queue | 
					
						
							|  |  |  | 	// index is attached to. | 
					
						
							|  |  |  | 	ptr unsafe.Pointer | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// name is the actual name of this | 
					
						
							|  |  |  | 	// index, which is the unparsed | 
					
						
							|  |  |  | 	// string value of contained fields. | 
					
						
							|  |  |  | 	name string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	// backing data store of the index, containing | 
					
						
							|  |  |  | 	// the cached results contained within wrapping | 
					
						
							|  |  |  | 	// index_entry{} which also contains the exact | 
					
						
							|  |  |  | 	// key each result is stored under. the hash map | 
					
						
							|  |  |  | 	// only keys by the xxh3 hash checksum for speed. | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | 	data hashmap | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// struct fields encompassed by | 
					
						
							|  |  |  | 	// keys (+ hashes) of this index. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	fields []struct_field | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// index flags: | 
					
						
							|  |  |  | 	// - 1 << 0 = unique | 
					
						
							|  |  |  | 	// - 1 << 1 = allow zero | 
					
						
							|  |  |  | 	flags uint8 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // Name returns the receiving Index name. | 
					
						
							|  |  |  | func (i *Index) Name() string { | 
					
						
							|  |  |  | 	return i.name | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // Key generates Key{} from given parts for | 
					
						
							|  |  |  | // the type of lookup this Index uses in cache. | 
					
						
							|  |  |  | // NOTE: panics on incorrect no. parts / types given. | 
					
						
							|  |  |  | func (i *Index) Key(parts ...any) Key { | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | 	ptrs := make([]unsafe.Pointer, len(parts)) | 
					
						
							|  |  |  | 	for x, part := range parts { | 
					
						
							|  |  |  | 		ptrs[x] = eface_data(part) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	buf := new_buffer() | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | 	key := i.key(buf, ptrs) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	free_buffer(buf) | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 	return Key{ | 
					
						
							|  |  |  | 		raw: parts, | 
					
						
							|  |  |  | 		key: key, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // Keys generates []Key{} from given (multiple) parts | 
					
						
							|  |  |  | // for the type of lookup this Index uses in the cache. | 
					
						
							|  |  |  | // NOTE: panics on incorrect no. parts / types given. | 
					
						
							|  |  |  | func (i *Index) Keys(parts ...[]any) []Key { | 
					
						
							|  |  |  | 	keys := make([]Key, 0, len(parts)) | 
					
						
							|  |  |  | 	buf := new_buffer() | 
					
						
							|  |  |  | 	for _, parts := range parts { | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | 		ptrs := make([]unsafe.Pointer, len(parts)) | 
					
						
							|  |  |  | 		for x, part := range parts { | 
					
						
							|  |  |  | 			ptrs[x] = eface_data(part) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		key := i.key(buf, ptrs) | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 		if key == "" { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 		keys = append(keys, Key{ | 
					
						
							|  |  |  | 			raw: parts, | 
					
						
							|  |  |  | 			key: key, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	free_buffer(buf) | 
					
						
							|  |  |  | 	return keys | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // init will initialize the cache with given type, config and capacity. | 
					
						
							|  |  |  | func (i *Index) init(t reflect.Type, cfg IndexConfig, cap int) { | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	// The only 2 types we support are | 
					
						
							|  |  |  | 	// structs, and ptrs to a struct. | 
					
						
							|  |  |  | 	case t.Kind() == reflect.Struct: | 
					
						
							|  |  |  | 	case t.Kind() == reflect.Pointer && | 
					
						
							|  |  |  | 		t.Elem().Kind() == reflect.Struct: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic("index only support struct{} and *struct{}") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set name from the raw | 
					
						
							|  |  |  | 	// struct fields string. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	i.name = cfg.Fields | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set struct flags. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if cfg.AllowZero { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 		set_allow_zero(&i.flags) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if !cfg.Multiple { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 		set_is_unique(&i.flags) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Split to get containing struct fields. | 
					
						
							|  |  |  | 	fields := strings.Split(cfg.Fields, ",") | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Preallocate expected struct field slice. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	i.fields = make([]struct_field, len(fields)) | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 	for x, name := range fields { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Split name to account for nesting. | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 		names := strings.Split(name, ".") | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Look for usable struct field. | 
					
						
							|  |  |  | 		i.fields[x] = find_field(t, names) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | 	// Initialize store for | 
					
						
							|  |  |  | 	// index_entry lists. | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | 	i.data.Init(cap) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // get_one will fetch one indexed item under key. | 
					
						
							|  |  |  | func (i *Index) get_one(key Key) *indexed_item { | 
					
						
							|  |  |  | 	// Get list at hash. | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 	l := i.data.Get(key.key) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	if l == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Extract entry from first list elem. | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	entry := (*index_entry)(l.head.data) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return entry.item | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // get will fetch all indexed items under key, passing each to hook. | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | func (i *Index) get(key string, hook func(*indexed_item)) { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if hook == nil { | 
					
						
							|  |  |  | 		panic("nil hook") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Get list at hash. | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 	l := i.data.Get(key) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	if l == nil { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 	// Iterate the list. | 
					
						
							|  |  |  | 	for elem := l.head; // | 
					
						
							|  |  |  | 	elem != nil;        // | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// Get next before | 
					
						
							|  |  |  | 		// any modification. | 
					
						
							|  |  |  | 		next := elem.next | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Extract element entry + item. | 
					
						
							|  |  |  | 		entry := (*index_entry)(elem.data) | 
					
						
							|  |  |  | 		item := entry.item | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Pass to hook. | 
					
						
							|  |  |  | 		hook(item) | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Set next. | 
					
						
							|  |  |  | 		elem = next | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // key uses hasher to generate Key{} from given raw parts. | 
					
						
							| 
									
										
										
										
											2024-06-21 15:43:17 +00:00
										 |  |  | func (i *Index) key(buf *byteutil.Buffer, parts []unsafe.Pointer) string { | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 	buf.B = buf.B[:0] | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if len(parts) != len(i.fields) { | 
					
						
							|  |  |  | 		panicf("incorrect number key parts: want=%d received=%d", | 
					
						
							|  |  |  | 			len(i.fields), | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 			len(parts), | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !allow_zero(i.flags) { | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 		for x, field := range i.fields { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 			before := len(buf.B) | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 			buf.B = field.mangle(buf.B, parts[x]) | 
					
						
							| 
									
										
										
										
											2024-05-13 08:05:46 +00:00
										 |  |  | 			if string(buf.B[before:]) == field.zerostr { | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 				return "" | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 			buf.B = append(buf.B, '.') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2024-04-11 10:46:08 +01:00
										 |  |  | 		for x, field := range i.fields { | 
					
						
							|  |  |  | 			buf.B = field.mangle(buf.B, parts[x]) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 			buf.B = append(buf.B, '.') | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 	return string(buf.B) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // append will append the given index entry to appropriate | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | // doubly-linked-list in index hashmap. this handles case of | 
					
						
							|  |  |  | // overwriting "unique" index entries, and removes from given | 
					
						
							|  |  |  | // outer linked-list in the case that it is no longer indexed. | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | func (i *Index) append(key string, item *indexed_item) (evicted *indexed_item) { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Look for existing. | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 	l := i.data.Get(key) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if l == nil { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Allocate new. | 
					
						
							|  |  |  | 		l = new_list() | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 		i.data.Put(key, l) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	} else if is_unique(i.flags) { | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Remove head. | 
					
						
							|  |  |  | 		elem := l.head | 
					
						
							|  |  |  | 		l.remove(elem) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 		// Drop index from inner item, | 
					
						
							|  |  |  | 		// catching the evicted item. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		e := (*index_entry)(elem.data) | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | 		evicted = e.item | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 		evicted.drop_index(e) | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Free unused entry. | 
					
						
							|  |  |  | 		free_index_entry(e) | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | 		if len(evicted.indexed) != 0 { | 
					
						
							|  |  |  | 			// Evicted is still stored | 
					
						
							|  |  |  | 			// under index, don't return. | 
					
						
							|  |  |  | 			evicted = nil | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Prepare new index entry. | 
					
						
							|  |  |  | 	entry := new_index_entry() | 
					
						
							|  |  |  | 	entry.item = item | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	entry.key = key | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	entry.index = i | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Add ourselves to item's index tracker. | 
					
						
							|  |  |  | 	item.indexed = append(item.indexed, entry) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Add entry to index list. | 
					
						
							|  |  |  | 	l.push_front(&entry.elem) | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | 	return | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // delete will remove all indexed items under key, passing each to hook. | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | func (i *Index) delete(key string, hook func(*indexed_item)) { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if hook == nil { | 
					
						
							|  |  |  | 		panic("nil hook") | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	// Get list at hash. | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 	l := i.data.Get(key) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	if l == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 	// Delete at hash. | 
					
						
							|  |  |  | 	i.data.Delete(key) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 	// Iterate the list. | 
					
						
							|  |  |  | 	for elem := l.head; // | 
					
						
							|  |  |  | 	elem != nil;        // | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// Get next before | 
					
						
							|  |  |  | 		// any modification. | 
					
						
							|  |  |  | 		next := elem.next | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 		// Remove elem. | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		l.remove(elem) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Extract element entry + item. | 
					
						
							|  |  |  | 		entry := (*index_entry)(elem.data) | 
					
						
							|  |  |  | 		item := entry.item | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Drop index from item. | 
					
						
							|  |  |  | 		item.drop_index(entry) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Free now-unused entry. | 
					
						
							|  |  |  | 		free_index_entry(entry) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Pass to hook. | 
					
						
							|  |  |  | 		hook(item) | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Set next. | 
					
						
							|  |  |  | 		elem = next | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Release list. | 
					
						
							|  |  |  | 	free_list(l) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // delete_entry deletes the given index entry. | 
					
						
							|  |  |  | func (i *Index) delete_entry(entry *index_entry) { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	// Get list at hash sum. | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:46 +00:00
										 |  |  | 	l := i.data.Get(entry.key) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	if l == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Remove list entry. | 
					
						
							|  |  |  | 	l.remove(&entry.elem) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	if l.len == 0 { | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 		// Remove entry from map. | 
					
						
							|  |  |  | 		i.data.Delete(entry.key) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 		// Release list. | 
					
						
							|  |  |  | 		free_list(l) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// Drop this index from item. | 
					
						
							|  |  |  | 	entry.item.drop_index(entry) | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // index_entry represents a single entry | 
					
						
							|  |  |  | // in an Index{}, where it will be accessible | 
					
						
							|  |  |  | // by Key{} pointing to a containing list{}. | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | type index_entry struct { | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// list elem that entry is stored | 
					
						
							|  |  |  | 	// within, under containing index. | 
					
						
							|  |  |  | 	// elem.data is ptr to index_entry. | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	elem list_elem | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	// index this is stored in. | 
					
						
							|  |  |  | 	index *Index | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// underlying indexed item. | 
					
						
							|  |  |  | 	item *indexed_item | 
					
						
							| 
									
										
										
										
											2024-08-14 12:08:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// raw cache key | 
					
						
							|  |  |  | 	// for this entry. | 
					
						
							|  |  |  | 	key string | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | var index_entry_pool sync.Pool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // new_index_entry returns a new prepared index_entry. | 
					
						
							|  |  |  | func new_index_entry() *index_entry { | 
					
						
							|  |  |  | 	v := index_entry_pool.Get() | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	if v == nil { | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 		e := new(index_entry) | 
					
						
							|  |  |  | 		e.elem.data = unsafe.Pointer(e) | 
					
						
							|  |  |  | 		v = e | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	entry := v.(*index_entry) | 
					
						
							|  |  |  | 	return entry | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | // free_index_entry releases the index_entry. | 
					
						
							|  |  |  | func free_index_entry(entry *index_entry) { | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 	if entry.elem.next != nil || | 
					
						
							|  |  |  | 		entry.elem.prev != nil { | 
					
						
							| 
									
										
										
										
											2025-03-12 20:33:35 +00:00
										 |  |  | 		should_not_reach(false) | 
					
						
							| 
									
										
										
										
											2024-10-02 10:58:20 +00:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-06-03 11:03:35 +00:00
										 |  |  | 	entry.key = "" | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 	entry.index = nil | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | 	entry.item = nil | 
					
						
							|  |  |  | 	index_entry_pool.Put(entry) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | func is_unique(f uint8) bool { | 
					
						
							|  |  |  | 	const mask = uint8(1) << 0 | 
					
						
							|  |  |  | 	return f&mask != 0 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:13:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-02 11:03:40 +01:00
										 |  |  | func set_is_unique(f *uint8) { | 
					
						
							|  |  |  | 	const mask = uint8(1) << 0 | 
					
						
							|  |  |  | 	(*f) |= mask | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func allow_zero(f uint8) bool { | 
					
						
							|  |  |  | 	const mask = uint8(1) << 1 | 
					
						
							|  |  |  | 	return f&mask != 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func set_allow_zero(f *uint8) { | 
					
						
							|  |  |  | 	const mask = uint8(1) << 1 | 
					
						
							|  |  |  | 	(*f) |= mask | 
					
						
							| 
									
										
										
										
											2024-01-19 12:57:29 +00:00
										 |  |  | } |