diff options
| author | 2024-06-12 07:55:40 +0200 | |
|---|---|---|
| committer | 2024-06-12 07:55:40 +0200 | |
| commit | aca6b2cff7195c4387bfbec3f55a68c3c201792a (patch) | |
| tree | 0dbbe78191ec30c9b25997da3be8fc5b004c6271 | |
| parent | Merge pull request 'Fix typos on manpages' (#182) from sergiodj/snac2:typos-f... (diff) | |
| download | snac2-aca6b2cff7195c4387bfbec3f55a68c3c201792a.tar.gz snac2-aca6b2cff7195c4387bfbec3f55a68c3c201792a.tar.xz snac2-aca6b2cff7195c4387bfbec3f55a68c3c201792a.zip | |
Backport from xs (faster dicts).
| -rw-r--r-- | xs.h | 250 | ||||
| -rw-r--r-- | xs_version.h | 2 |
2 files changed, 168 insertions, 84 deletions
| @@ -1040,12 +1040,29 @@ xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *va | |||
| 1040 | 1040 | ||
| 1041 | /** dicts **/ | 1041 | /** dicts **/ |
| 1042 | 1042 | ||
| 1043 | typedef struct { | ||
| 1044 | int value_offset; /* offset to value (from dict start) */ | ||
| 1045 | int next; /* next node in sequential search */ | ||
| 1046 | int child[4]; /* child nodes in hashed search */ | ||
| 1047 | char key[]; /* C string key */ | ||
| 1048 | } ditem_hdr; | ||
| 1049 | |||
| 1050 | typedef struct { | ||
| 1051 | int size; /* size of full dict (_XS_TYPE_SIZE) */ | ||
| 1052 | int first; /* first node for sequential search */ | ||
| 1053 | int root; /* root node for hashed search */ | ||
| 1054 | ditem_hdr ditems[]; /* the ditems */ | ||
| 1055 | } dict_hdr; | ||
| 1056 | |||
| 1057 | |||
| 1043 | xs_dict *xs_dict_new(void) | 1058 | xs_dict *xs_dict_new(void) |
| 1044 | /* creates a new dict */ | 1059 | /* creates a new dict */ |
| 1045 | { | 1060 | { |
| 1046 | int sz = 1 + _XS_TYPE_SIZE + 1; | 1061 | /* size of dict */ |
| 1062 | int sz = 1 + sizeof(dict_hdr); | ||
| 1063 | |||
| 1047 | xs_dict *d = xs_realloc(NULL, sz); | 1064 | xs_dict *d = xs_realloc(NULL, sz); |
| 1048 | memset(d, XSTYPE_EOM, sz); | 1065 | memset(d, '\0', sz); |
| 1049 | 1066 | ||
| 1050 | d[0] = XSTYPE_DICT; | 1067 | d[0] = XSTYPE_DICT; |
| 1051 | _xs_put_size(d, sz); | 1068 | _xs_put_size(d, sz); |
| @@ -1053,140 +1070,207 @@ xs_dict *xs_dict_new(void) | |||
| 1053 | return d; | 1070 | return d; |
| 1054 | } | 1071 | } |
| 1055 | 1072 | ||
| 1056 | 1073 | static int *_xs_dict_locate(const xs_dict *dict, const char *key) | |
| 1057 | xs_dict *_xs_dict_write_keyval(xs_dict *dict, int offset, const xs_str *key, const xs_val *value) | 1074 | /* locates a ditem */ |
| 1058 | /* adds a new keyval to the dict */ | ||
| 1059 | { | 1075 | { |
| 1060 | XS_ASSERT_TYPE(dict, XSTYPE_DICT); | 1076 | unsigned int h = xs_hash_func(key, strlen(key)); |
| 1061 | XS_ASSERT_TYPE(key, XSTYPE_STRING); | ||
| 1062 | 1077 | ||
| 1063 | if (value == NULL) | 1078 | /* start from the root */ |
| 1064 | value = xs_stock(XSTYPE_NULL); | 1079 | dict_hdr *dh = (dict_hdr *)(dict + 1); |
| 1080 | int *off = &dh->root; | ||
| 1065 | 1081 | ||
| 1066 | dict = xs_expand(dict, offset, xs_keyval_size(key, value)); | 1082 | while (*off) { |
| 1083 | /* pointer to ditem */ | ||
| 1084 | ditem_hdr *di = (ditem_hdr *)(dict + *off); | ||
| 1067 | 1085 | ||
| 1068 | xs_keyval_make(&dict[offset], key, value); | 1086 | /* pointer to the key */ |
| 1087 | const char *d_key = di->key; | ||
| 1069 | 1088 | ||
| 1070 | return dict; | 1089 | if (strcmp(key, d_key) == 0) |
| 1071 | } | 1090 | break; |
| 1072 | 1091 | ||
| 1092 | off = &di->child[h >> 30]; | ||
| 1093 | h <<= 2; | ||
| 1094 | } | ||
| 1073 | 1095 | ||
| 1074 | xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) | 1096 | return off; |
| 1075 | /* appends a memory block to the dict */ | ||
| 1076 | { | ||
| 1077 | return _xs_dict_write_keyval(dict, xs_size(dict) - 1, key, value); | ||
| 1078 | } | 1097 | } |
| 1079 | 1098 | ||
| 1080 | 1099 | ||
| 1081 | xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) | 1100 | xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value) |
| 1082 | /* prepends a memory block to the dict */ | 1101 | /* sets a key/value pair */ |
| 1083 | { | 1102 | { |
| 1084 | return _xs_dict_write_keyval(dict, 1 + _XS_TYPE_SIZE, key, value); | 1103 | if (value == NULL) |
| 1085 | } | 1104 | value = xs_stock(XSTYPE_NULL); |
| 1086 | 1105 | ||
| 1106 | if (xs_type(dict) == XSTYPE_DICT) { | ||
| 1107 | int *o = _xs_dict_locate(dict, key); | ||
| 1108 | int end = xs_size(dict); | ||
| 1087 | 1109 | ||
| 1088 | int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt) | 1110 | if (!*o) { |
| 1089 | /* iterates a dict, with context */ | 1111 | /* ditem does not exist yet: append to the end */ |
| 1090 | { | 1112 | *o = end; |
| 1091 | if (xs_type(dict) != XSTYPE_DICT) | ||
| 1092 | return 0; | ||
| 1093 | 1113 | ||
| 1094 | int goon = 1; | 1114 | int ksz = xs_size(key); |
| 1115 | int vsz = xs_size(value); | ||
| 1116 | int dsz = sizeof(ditem_hdr) + ksz + vsz; | ||
| 1095 | 1117 | ||
| 1096 | char *p = (char *)dict; | 1118 | /* open room in the dict for the full ditem */ |
| 1119 | dict = xs_expand(dict, end, dsz); | ||
| 1097 | 1120 | ||
| 1098 | /* skip the start of the list */ | 1121 | dict_hdr *dh = (dict_hdr *)(dict + 1); |
| 1099 | if (*ctxt == 0) | ||
| 1100 | *ctxt = 1 + _XS_TYPE_SIZE; | ||
| 1101 | 1122 | ||
| 1102 | p += *ctxt; | 1123 | /* build the ditem */ |
| 1124 | ditem_hdr *di = (ditem_hdr *)(dict + end); | ||
| 1125 | memset(di, '\0', dsz); | ||
| 1103 | 1126 | ||
| 1104 | /* an element? */ | 1127 | /* set the offset to the value */ |
| 1105 | if (xs_type(p) == XSTYPE_KEYVAL) { | 1128 | di->value_offset = end + sizeof(ditem_hdr) + ksz; |
| 1106 | p++; | ||
| 1107 | 1129 | ||
| 1108 | *key = p; | 1130 | /* copy the key */ |
| 1109 | p += xs_size(*key); | 1131 | memcpy(di->key, key, ksz); |
| 1110 | 1132 | ||
| 1111 | *value = p; | 1133 | /* copy the value */ |
| 1112 | p += xs_size(*value); | 1134 | memcpy(dict + di->value_offset, value, vsz); |
| 1113 | } | 1135 | |
| 1114 | else { | 1136 | /* chain to the sequential list */ |
| 1115 | /* end of list */ | 1137 | di->next = dh->first; |
| 1116 | goon = 0; | 1138 | dh->first = end; |
| 1139 | } | ||
| 1140 | else { | ||
| 1141 | /* ditem already exists */ | ||
| 1142 | ditem_hdr *di = (ditem_hdr *)(dict + *o); | ||
| 1143 | |||
| 1144 | /* get pointer to the value offset */ | ||
| 1145 | int *i = &di->value_offset; | ||
| 1146 | |||
| 1147 | /* deleted? recover offset */ | ||
| 1148 | if (*i < 0) | ||
| 1149 | *i *= -1; | ||
| 1150 | |||
| 1151 | /* get old value */ | ||
| 1152 | xs_val *o_value = dict + *i; | ||
| 1153 | |||
| 1154 | /* will new value fit over the old one? */ | ||
| 1155 | if (xs_size(value) <= xs_size(o_value)) { | ||
| 1156 | /* just overwrite */ | ||
| 1157 | /* (difference is leaked inside the dict) */ | ||
| 1158 | memcpy(o_value, value, xs_size(value)); | ||
| 1159 | } | ||
| 1160 | else { | ||
| 1161 | /* not enough room: new value will live at the end of the dict */ | ||
| 1162 | /* (old value is leaked inside the dict) */ | ||
| 1163 | *i = end; | ||
| 1164 | |||
| 1165 | dict = xs_insert(dict, end, value); | ||
| 1166 | } | ||
| 1167 | } | ||
| 1117 | } | 1168 | } |
| 1118 | 1169 | ||
| 1119 | /* store back the pointer */ | 1170 | return dict; |
| 1120 | *ctxt = p - dict; | 1171 | } |
| 1121 | 1172 | ||
| 1122 | return goon; | 1173 | |
| 1174 | xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) | ||
| 1175 | /* just an alias (for this implementation it's the same) */ | ||
| 1176 | { | ||
| 1177 | return xs_dict_set(dict, key, value); | ||
| 1123 | } | 1178 | } |
| 1124 | 1179 | ||
| 1125 | 1180 | ||
| 1126 | const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def) | 1181 | xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) |
| 1127 | /* returns the value directed by key, or the default value */ | 1182 | /* just an alias (for this implementation it's the same) */ |
| 1128 | { | 1183 | { |
| 1129 | XS_ASSERT_TYPE(dict, XSTYPE_DICT); | 1184 | return xs_dict_set(dict, key, value); |
| 1130 | XS_ASSERT_TYPE(key, XSTYPE_STRING); | 1185 | } |
| 1131 | 1186 | ||
| 1132 | const xs_str *k; | ||
| 1133 | const xs_val *v; | ||
| 1134 | int c = 0; | ||
| 1135 | 1187 | ||
| 1136 | while (xs_dict_next(dict, &k, &v, &c)) { | 1188 | xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key) |
| 1137 | if (strcmp(k, key) == 0) | 1189 | /* deletes a key/value pair */ |
| 1138 | return v; | 1190 | { |
| 1191 | if (xs_type(dict) == XSTYPE_DICT) { | ||
| 1192 | int *o = _xs_dict_locate(dict, key); | ||
| 1193 | |||
| 1194 | if (*o) { | ||
| 1195 | /* found ditem */ | ||
| 1196 | ditem_hdr *di = (ditem_hdr *)(dict + *o); | ||
| 1197 | |||
| 1198 | /* deleted ditems have a negative value offset */ | ||
| 1199 | di->value_offset *= -1; | ||
| 1200 | } | ||
| 1139 | } | 1201 | } |
| 1140 | 1202 | ||
| 1141 | return (xs_val *)def; | 1203 | return dict; |
| 1142 | } | 1204 | } |
| 1143 | 1205 | ||
| 1144 | 1206 | ||
| 1145 | xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key) | 1207 | const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_str *def) |
| 1146 | /* deletes a key */ | 1208 | /* gets a value by key, or returns def */ |
| 1147 | { | 1209 | { |
| 1148 | XS_ASSERT_TYPE(dict, XSTYPE_DICT); | 1210 | if (xs_type(dict) == XSTYPE_DICT) { |
| 1149 | XS_ASSERT_TYPE(key, XSTYPE_STRING); | 1211 | int *o = _xs_dict_locate(dict, key); |
| 1150 | |||
| 1151 | const xs_str *k; | ||
| 1152 | const xs_val *v; | ||
| 1153 | int c = 0; | ||
| 1154 | 1212 | ||
| 1155 | while (xs_dict_next(dict, &k, &v, &c)) { | 1213 | if (*o) { |
| 1156 | if (strcmp(k, key) == 0) { | 1214 | /* found ditem */ |
| 1157 | /* the address of the item is just behind the key */ | 1215 | ditem_hdr *di = (ditem_hdr *)(dict + *o); |
| 1158 | char *i = (char *)k - 1; | ||
| 1159 | 1216 | ||
| 1160 | dict = xs_collapse(dict, i - dict, xs_size(i)); | 1217 | if (di->value_offset > 0) |
| 1161 | break; | 1218 | return dict + di->value_offset; |
| 1162 | } | 1219 | } |
| 1163 | } | 1220 | } |
| 1164 | 1221 | ||
| 1165 | return dict; | 1222 | return def; |
| 1166 | } | 1223 | } |
| 1167 | 1224 | ||
| 1168 | 1225 | ||
| 1169 | xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data) | 1226 | int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt) |
| 1170 | /* sets (replaces) a key */ | 1227 | /* dict iterator, with context */ |
| 1171 | { | 1228 | { |
| 1172 | XS_ASSERT_TYPE(dict, XSTYPE_DICT); | 1229 | if (xs_type(dict) != XSTYPE_DICT) |
| 1173 | XS_ASSERT_TYPE(key, XSTYPE_STRING); | 1230 | return 0; |
| 1174 | 1231 | ||
| 1175 | /* delete the possibly existing key */ | 1232 | if (*ctxt == 0) { |
| 1176 | dict = xs_dict_del(dict, key); | 1233 | /* at the beginning: get the first sequential item */ |
| 1234 | const dict_hdr *dh = (dict_hdr *)(dict + 1); | ||
| 1235 | *ctxt = dh->first; | ||
| 1236 | } | ||
| 1177 | 1237 | ||
| 1178 | /* add the data */ | 1238 | *value = NULL; |
| 1179 | dict = xs_dict_append(dict, key, data); | ||
| 1180 | 1239 | ||
| 1181 | return dict; | 1240 | while (*value == NULL && *ctxt > 0) { |
| 1241 | const ditem_hdr *di = (ditem_hdr *)(dict + *ctxt); | ||
| 1242 | |||
| 1243 | /* get value */ | ||
| 1244 | if (di->value_offset > 0) { | ||
| 1245 | *value = (xs_val *)(dict + di->value_offset); | ||
| 1246 | |||
| 1247 | /* get key */ | ||
| 1248 | *key = (xs_str *)&di->key; | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | /* get offset to next ditem */ | ||
| 1252 | *ctxt = di->next ? di->next : -1; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | return *value != NULL; | ||
| 1182 | } | 1256 | } |
| 1183 | 1257 | ||
| 1184 | 1258 | ||
| 1185 | xs_dict *xs_dict_gc(xs_dict *dict) | 1259 | xs_dict *xs_dict_gc(xs_dict *dict) |
| 1186 | /* collects garbage (leaked values) inside a dict */ | 1260 | /* collects garbage (leaked values) inside a dict */ |
| 1187 | { | 1261 | { |
| 1188 | /* this kind of dicts does not get garbage */ | 1262 | xs_dict *nd = xs_dict_new(); |
| 1189 | return dict; | 1263 | const xs_str *k; |
| 1264 | const xs_val *v; | ||
| 1265 | int c = 0; | ||
| 1266 | |||
| 1267 | /* shamelessly create a new dict with the same content */ | ||
| 1268 | while (xs_dict_next(dict, &k, &v, &c)) | ||
| 1269 | nd = xs_dict_set(nd, k, v); | ||
| 1270 | |||
| 1271 | xs_free(dict); | ||
| 1272 | |||
| 1273 | return nd; | ||
| 1190 | } | 1274 | } |
| 1191 | 1275 | ||
| 1192 | 1276 | ||
diff --git a/xs_version.h b/xs_version.h index 62f2aca..d647240 100644 --- a/xs_version.h +++ b/xs_version.h | |||
| @@ -1 +1 @@ | |||
| /* efb85fa3768a86f1609c520f0c86e8f87239b695 2024-05-27T08:24:40+02:00 */ | /* 4595e864ae31bc59cca0fff38bd2bac798c2b038 2024-06-08T19:50:32+02:00 */ | ||