map: use untyped keys for DenseArray and interleave keys and values (#7142)
							parent
							
								
									6b7d7cee0c
								
							
						
					
					
						commit
						0d28f12c54
					
				|  | @ -1,7 +1,7 @@ | |||
| module builtin | ||||
| 
 | ||||
| // <string.h>
 | ||||
| fn C.memcpy(byteptr, byteptr, int) voidptr | ||||
| fn C.memcpy(dest byteptr, src byteptr, n int) voidptr | ||||
| 
 | ||||
| fn C.memcmp(byteptr, byteptr, int) int | ||||
| 
 | ||||
|  |  | |||
|  | @ -94,107 +94,92 @@ fn fast_string_eq(a string, b string) bool { | |||
| 
 | ||||
| // Dynamic array with very low growth factor
 | ||||
| struct DenseArray { | ||||
| 	key_bytes   int | ||||
| 	value_bytes int | ||||
| 	slot_bytes  int // sum of 2 fields above
 | ||||
| mut: | ||||
| 	cap      u32 | ||||
| 	len      u32 | ||||
| 	deletes  u32 | ||||
| 	keys     &string | ||||
| 	values   byteptr | ||||
| 	cap      int | ||||
| 	len      int | ||||
| 	deletes  u32 // count
 | ||||
| 	data     byteptr // array of interspersed key data and value data
 | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| [unsafe] | ||||
| fn new_dense_array(value_bytes int) DenseArray { | ||||
| 	s8size := int(8 * sizeof(string)) | ||||
| fn new_dense_array(key_bytes int, value_bytes int) DenseArray { | ||||
| 	slot_bytes := key_bytes + value_bytes | ||||
| 	cap := 8 | ||||
| 	return DenseArray{ | ||||
| 		key_bytes: key_bytes | ||||
| 		value_bytes: value_bytes | ||||
| 		cap: 8 | ||||
| 		slot_bytes: slot_bytes | ||||
| 		cap: cap | ||||
| 		len: 0 | ||||
| 		deletes: 0 | ||||
| 		keys: &string(malloc(s8size)) | ||||
| 		values: malloc(8 * value_bytes) | ||||
| 		data: malloc(cap * slot_bytes)  | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (d &DenseArray) key(i int) voidptr { | ||||
| 	return unsafe {voidptr(d.keys + i)} | ||||
| 	return unsafe {d.data + i * d.slot_bytes} | ||||
| } | ||||
| 
 | ||||
| // for cgen
 | ||||
| [inline] | ||||
| fn (d &DenseArray) value(i int) voidptr { | ||||
| 	return unsafe {voidptr(d.values + i * d.value_bytes)} | ||||
| 	return unsafe {d.data + i * d.slot_bytes + d.key_bytes} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| fn (d &DenseArray) has_index(i int) bool { | ||||
| 	pkey := unsafe {d.keys + i} | ||||
| 	// assume string keys for now
 | ||||
| 	pkey := unsafe {&string(d.key(i))} | ||||
| 	return pkey.str != 0 | ||||
| } | ||||
| 
 | ||||
| // Push element to array and return index
 | ||||
| // The growth-factor is roughly 1.125 `(x + (x >> 3))`
 | ||||
| [inline] | ||||
| fn (mut d DenseArray) push(key string, value voidptr) u32 { | ||||
| fn (mut d DenseArray) push(key voidptr, value voidptr) int { | ||||
| 	if d.cap == d.len { | ||||
| 		d.cap += d.cap >> 3 | ||||
| 		unsafe { | ||||
| 			x := v_realloc(byteptr(d.keys), int(sizeof(string) * d.cap)) | ||||
| 			d.keys = &string(x) | ||||
| 			d.values = v_realloc(byteptr(d.values), d.value_bytes * int(d.cap)) | ||||
| 			d.data = v_realloc(d.data, d.slot_bytes * d.cap) | ||||
| 		} | ||||
| 	} | ||||
| 	push_index := d.len | ||||
| 	unsafe { | ||||
| 		d.keys[push_index] = key | ||||
| 		C.memcpy(d.values + push_index * u32(d.value_bytes), value, d.value_bytes) | ||||
| 		ptr := d.key(push_index) | ||||
| 		C.memcpy(ptr, key, d.key_bytes) | ||||
| 		C.memcpy(byteptr(ptr) + d.key_bytes, value, d.value_bytes) | ||||
| 	} | ||||
| 	d.len++ | ||||
| 	return push_index | ||||
| } | ||||
| 
 | ||||
| fn (d DenseArray) get(i int) voidptr { | ||||
| 	$if !no_bounds_checking? { | ||||
| 		if i < 0 || i >= int(d.len) { | ||||
| 			panic('DenseArray.get: index out of range (i == $i, d.len == $d.len)') | ||||
| 		} | ||||
| 	} | ||||
| 	unsafe { | ||||
| 		return byteptr(d.keys) + i * int(sizeof(string)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Move all zeros to the end of the array and resize array
 | ||||
| fn (mut d DenseArray) zeros_to_end() { | ||||
| 	mut tmp_value := malloc(d.value_bytes) | ||||
| 	mut count := u32(0) | ||||
| 	for i in 0 .. int(d.len) { | ||||
| 		if unsafe {d.keys[i]}.str != 0 { | ||||
| 			// swap keys
 | ||||
| 	// TODO alloca?
 | ||||
| 	mut tmp_buf := malloc(d.slot_bytes) | ||||
| 	mut count := 0 | ||||
| 	for i in 0 .. d.len { | ||||
| 		if d.has_index(i) { | ||||
| 			// swap (TODO: optimize)
 | ||||
| 			unsafe { | ||||
| 				tmp_key := d.keys[count] | ||||
| 				d.keys[count] = d.keys[i] | ||||
| 				d.keys[i] = tmp_key | ||||
| 			} | ||||
| 			// swap values (TODO: optimize)
 | ||||
| 			unsafe { | ||||
| 				C.memcpy(tmp_value, d.values + count * u32(d.value_bytes), d.value_bytes) | ||||
| 				C.memcpy(d.values + count * u32(d.value_bytes), d.values + i * d.value_bytes, d.value_bytes) | ||||
| 				C.memcpy(d.values + i * d.value_bytes, tmp_value, d.value_bytes) | ||||
| 				C.memcpy(tmp_buf, d.key(count), d.slot_bytes) | ||||
| 				C.memcpy(d.key(count), d.key(i), d.slot_bytes) | ||||
| 				C.memcpy(d.key(i), tmp_buf, d.slot_bytes) | ||||
| 			} | ||||
| 			count++ | ||||
| 		} | ||||
| 	} | ||||
| 	free(tmp_value) | ||||
| 	free(tmp_buf) | ||||
| 	d.deletes = 0 | ||||
| 	d.len = count | ||||
| 	d.cap = if count < 8 { u32(8) } else { count } | ||||
| 	d.cap = if count < 8 { 8 } else { count } | ||||
| 	unsafe { | ||||
| 		x := v_realloc(byteptr(d.keys), int(sizeof(string) * d.cap)) | ||||
| 		d.keys = &string(x) | ||||
| 		d.values = v_realloc(byteptr(d.values), d.value_bytes * int(d.cap)) | ||||
| 		d.data = v_realloc(d.data, d.slot_bytes * d.cap) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -229,7 +214,7 @@ fn new_map_1(value_bytes int) map { | |||
| 		cap: init_cap | ||||
| 		cached_hashbits: max_cached_hashbits | ||||
| 		shift: init_log_capicity | ||||
| 		key_values: new_dense_array(value_bytes) | ||||
| 		key_values: new_dense_array(int(sizeof(string)), value_bytes) | ||||
| 		metas: &u32(vcalloc(metasize)) | ||||
| 		extra_metas: extra_metas_inc | ||||
| 		len: 0 | ||||
|  | @ -324,10 +309,12 @@ fn (mut m map) set(k string, value voidptr) { | |||
| 	index,meta = m.meta_less(index, meta) | ||||
| 	// While we might have a match
 | ||||
| 	for meta == unsafe {m.metas[index]} { | ||||
| 		kv_index := unsafe {m.metas[index + 1]} | ||||
| 		if fast_string_eq(key, unsafe {m.key_values.keys[kv_index]}) { | ||||
| 		kv_index := int(unsafe {m.metas[index + 1]}) | ||||
| 		pkey := unsafe {&string(m.key_values.key(kv_index))} | ||||
| 		if fast_string_eq(key, *pkey) { | ||||
| 			unsafe { | ||||
| 				C.memcpy(m.key_values.values + kv_index * u32(m.value_bytes), value, m.value_bytes) | ||||
| 				pval := pkey + 1 // skip string
 | ||||
| 				C.memcpy(pval, value, m.value_bytes) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
|  | @ -335,7 +322,7 @@ fn (mut m map) set(k string, value voidptr) { | |||
| 		meta += probe_inc | ||||
| 	} | ||||
| 	kv_index := m.key_values.push(key, value) | ||||
| 	m.meta_greater(index, meta, kv_index) | ||||
| 	m.meta_greater(index, meta, u32(kv_index)) | ||||
| 	m.len++ | ||||
| } | ||||
| 
 | ||||
|  | @ -367,13 +354,14 @@ fn (mut m map) rehash() { | |||
| 		m.metas = &u32(x) | ||||
| 		C.memset(m.metas, 0, meta_bytes) | ||||
| 	} | ||||
| 	for i := u32(0); i < m.key_values.len; i++ { | ||||
| 		if unsafe {m.key_values.keys[i]}.str == 0 { | ||||
| 	for i := 0; i < m.key_values.len; i++ { | ||||
| 		if !m.key_values.has_index(i) { | ||||
| 			continue | ||||
| 		} | ||||
| 		mut index,mut meta := m.key_to_index(unsafe {m.key_values.keys[i]}) | ||||
| 		pkey := unsafe {&string(m.key_values.key(i))} | ||||
| 		mut index,mut meta := m.key_to_index(*pkey) | ||||
| 		index,meta = m.meta_less(index, meta) | ||||
| 		m.meta_greater(index, meta, i) | ||||
| 		m.meta_greater(index, meta, u32(i)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -403,18 +391,17 @@ fn (mut m map) cached_rehash(old_cap u32) { | |||
| } | ||||
| 
 | ||||
| // This method is used for assignment operators. If the argument-key
 | ||||
| // does not exist in the map, it's added to the map along with the zero/dafault value.
 | ||||
| // does not exist in the map, it's added to the map along with the zero/default value.
 | ||||
| // If the key exists, its respective value is returned.
 | ||||
| fn (mut m map) get_and_set(key string, zero voidptr) voidptr { | ||||
| 	for { | ||||
| 		mut index,mut meta := m.key_to_index(key) | ||||
| 		for { | ||||
| 			if meta == unsafe {m.metas[index]} { | ||||
| 				kv_index := unsafe {m.metas[index + 1]} | ||||
| 				if fast_string_eq(key, unsafe {m.key_values.keys[kv_index]}) { | ||||
| 					unsafe { | ||||
| 						return voidptr(m.key_values.values + kv_index * u32(m.value_bytes)) | ||||
| 					} | ||||
| 				kv_index := int(unsafe {m.metas[index + 1]}) | ||||
| 				pkey := unsafe {&string(m.key_values.key(kv_index))} | ||||
| 				if fast_string_eq(key, *pkey) { | ||||
| 					return unsafe {byteptr(pkey) + m.key_values.key_bytes} | ||||
| 				} | ||||
| 			} | ||||
| 			index += 2 | ||||
|  | @ -435,11 +422,10 @@ fn (m map) get(key string, zero voidptr) voidptr { | |||
| 	mut index,mut meta := m.key_to_index(key) | ||||
| 	for { | ||||
| 		if meta == unsafe {m.metas[index]} { | ||||
| 			kv_index := unsafe {m.metas[index + 1]} | ||||
| 			if fast_string_eq(key, unsafe {m.key_values.keys[kv_index]}) { | ||||
| 				unsafe { | ||||
| 					return voidptr(m.key_values.values + kv_index * u32(m.value_bytes)) | ||||
| 				} | ||||
| 			kv_index := int(unsafe {m.metas[index + 1]}) | ||||
| 			pkey := unsafe {&string(m.key_values.key(kv_index))} | ||||
| 			if fast_string_eq(key, *pkey) { | ||||
| 				return unsafe {byteptr(pkey) + m.key_values.key_bytes} | ||||
| 			} | ||||
| 		} | ||||
| 		index += 2 | ||||
|  | @ -454,9 +440,10 @@ fn (m map) exists(key string) bool { | |||
| 	mut index,mut meta := m.key_to_index(key) | ||||
| 	for { | ||||
| 		if meta == unsafe {m.metas[index]} { | ||||
| 			kv_index := unsafe {m.metas[index + 1]} | ||||
| 			if fast_string_eq(key, unsafe {m.key_values.keys[kv_index]}) { | ||||
| 				return  true | ||||
| 			kv_index := int(unsafe {m.metas[index + 1]}) | ||||
| 			pkey := unsafe {&string(m.key_values.key(kv_index))} | ||||
| 			if fast_string_eq(key, *pkey) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 		index += 2 | ||||
|  | @ -472,8 +459,9 @@ pub fn (mut m map) delete(key string) { | |||
| 	index,meta = m.meta_less(index, meta) | ||||
| 	// Perform backwards shifting
 | ||||
| 	for meta == unsafe {m.metas[index]} { | ||||
| 		kv_index := unsafe {m.metas[index + 1]} | ||||
| 		if fast_string_eq(key, unsafe {m.key_values.keys[kv_index]}) { | ||||
| 		kv_index := int(unsafe {m.metas[index + 1]}) | ||||
| 		pkey := unsafe {&string(m.key_values.key(kv_index))} | ||||
| 		if fast_string_eq(key, *pkey) { | ||||
| 			for (unsafe {m.metas[index + 2]} >> hashbits) > 1 { | ||||
| 				unsafe { | ||||
| 					m.metas[index] = m.metas[index + 2] - probe_inc | ||||
|  | @ -488,8 +476,8 @@ pub fn (mut m map) delete(key string) { | |||
| 			m.key_values.deletes++ | ||||
| 			// Mark key as deleted
 | ||||
| 			unsafe { | ||||
| 				m.key_values.keys[kv_index].free() | ||||
| 				C.memset(&m.key_values.keys[kv_index], 0, sizeof(string)) | ||||
| 				(*pkey).free() | ||||
| 				C.memset(pkey, 0, sizeof(string)) | ||||
| 			} | ||||
| 			if m.key_values.len <= 32 { | ||||
| 				return | ||||
|  | @ -512,11 +500,12 @@ pub fn (mut m map) delete(key string) { | |||
| pub fn (m &map) keys() []string { | ||||
| 	mut keys := []string{ len:m.len } | ||||
| 	mut j := 0 | ||||
| 	for i := u32(0); i < m.key_values.len; i++ { | ||||
| 		if unsafe {m.key_values.keys[i]}.str == 0 { | ||||
| 	for i := 0; i < m.key_values.len; i++ { | ||||
| 		if !m.key_values.has_index(i) { | ||||
| 			continue | ||||
| 		} | ||||
| 		keys[j] = unsafe {m.key_values.keys[i]}.clone() | ||||
| 		pkey := unsafe {&string(m.key_values.key(i))} | ||||
| 		keys[j] = pkey.clone() | ||||
| 		j++ | ||||
| 	} | ||||
| 	return keys | ||||
|  | @ -524,20 +513,16 @@ pub fn (m &map) keys() []string { | |||
| 
 | ||||
| [unsafe] | ||||
| pub fn (d DenseArray) clone() DenseArray { | ||||
| 	ksize := int(d.cap * sizeof(string)) | ||||
| 	vsize := int(d.cap * u32(d.value_bytes)) | ||||
| 	res := DenseArray { | ||||
| 		key_bytes:   d.key_bytes | ||||
| 		value_bytes: d.value_bytes | ||||
| 		slot_bytes:  d.slot_bytes | ||||
| 		cap:         d.cap | ||||
| 		len:         d.len | ||||
| 		deletes:     d.deletes | ||||
| 		keys:        unsafe {&string(malloc(ksize))} | ||||
| 		values:      unsafe {byteptr(malloc(vsize))} | ||||
| 	} | ||||
| 	unsafe { | ||||
| 		C.memcpy(res.keys, d.keys, ksize) | ||||
| 		C.memcpy(res.values, d.values, vsize) | ||||
| 		data:        unsafe {memdup(d.data, d.cap * d.slot_bytes)} | ||||
| 	} | ||||
| 	// FIXME clone each key
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
|  | @ -552,7 +537,7 @@ pub fn (m map) clone() map { | |||
| 		key_values:      unsafe {m.key_values.clone()} | ||||
| 		metas:           &u32(malloc(metasize)) | ||||
| 		extra_metas:     m.extra_metas | ||||
| 		len:            m.len | ||||
| 		len:             m.len | ||||
| 	} | ||||
| 	unsafe { | ||||
| 		C.memcpy(res.metas, m.metas, metasize) | ||||
|  | @ -565,17 +550,17 @@ pub fn (m &map) free() { | |||
| 	unsafe { | ||||
| 		free(m.metas) | ||||
| 	} | ||||
| 	for i := u32(0); i < m.key_values.len; i++ { | ||||
| 		if unsafe {m.key_values.keys[i]}.str == 0 { | ||||
| 	for i := 0; i < m.key_values.len; i++ { | ||||
| 		if !m.key_values.has_index(i) { | ||||
| 			continue | ||||
| 		} | ||||
| 		unsafe { | ||||
| 			m.key_values.keys[i].free() | ||||
| 			pkey := &string(m.key_values.key(i)) | ||||
| 			(*pkey).free() | ||||
| 		} | ||||
| 	} | ||||
| 	unsafe { | ||||
| 		free(m.key_values.keys) | ||||
| 		free(m.key_values.values) | ||||
| 		free(m.key_values.data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -264,7 +264,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string) | |||
| 	g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp m, int indent_count) { /* gen_str_for_map */') | ||||
| 	g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.len*10);') | ||||
| 	g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _SLIT("{"));') | ||||
| 	g.auto_str_funcs.writeln('\tfor (unsigned int i = 0; i < m.key_values.len; ++i) {') | ||||
| 	g.auto_str_funcs.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {') | ||||
| 	g.auto_str_funcs.writeln('\t\tif (!DenseArray_has_index(&m.key_values, i)) { continue; }') | ||||
| 	g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);') | ||||
| 	g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, key));') | ||||
|  |  | |||
|  | @ -1233,7 +1233,7 @@ fn (mut g Gen) for_in(it ast.ForInStmt) { | |||
| 		g.write('$atmp_styp $atmp = ') | ||||
| 		g.expr(it.cond) | ||||
| 		g.writeln(';') | ||||
| 		g.writeln('for (int $idx = 0; $idx < (int)${atmp}.key_values.len; ++$idx) {') | ||||
| 		g.writeln('for (int $idx = 0; $idx < ${atmp}.key_values.len; ++$idx) {') | ||||
| 		g.writeln('\tif (!DenseArray_has_index(&${atmp}.key_values, $idx)) {continue;}') | ||||
| 		if it.key_var != '_' { | ||||
| 			key_styp := g.typ(it.key_type) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue