|
| 1 | +coclass 'jdictionary' |
| 2 | + |
| 3 | +SIZE_GROWTH_GEOMETRIC_STEP =: 2 |
| 4 | + |
| 5 | +create =: {{)m |
| 6 | +'index_type creation_parameters' =. y |
| 7 | + |
| 8 | +NB. Default values of params. |
| 9 | +keytype =: 4 |
| 10 | +keyshape =: i. 0 |
| 11 | +valuetype =: 4 |
| 12 | +valueshape =: i. 0 |
| 13 | +keyhash =: 16!:0`'' |
| 14 | +keycompare =: 16!:0`'' |
| 15 | +initsize =: 100 |
| 16 | +name =: '' |
| 17 | + |
| 18 | +if. (-: (index_type {.~ -@#)) 'concurrent' do. |
| 19 | + singlethreaded =. 0 |
| 20 | + index_type =. (- # ' concurrent') }. index_type |
| 21 | +else. |
| 22 | + singlethreaded =. 1 |
| 23 | +end. |
| 24 | + |
| 25 | +select. index_type NB. set up params for create, based on map type |
| 26 | +case. 'hash' do. |
| 27 | + itype =: 0 NB. index type 0 is hash |
| 28 | + occupancy =: 0.5 NB. default for occupancy |
| 29 | + NB. Parse params and update above attributes. |
| 30 | + parse^:(*@#) creation_parameters |
| 31 | + internal_parameters =. (0 , initsize , <. initsize % occupancy) ; singlethreaded ; (keytype ; keyshape) ; < (valuetype ; valueshape) |
| 32 | +case. 'tree' do. |
| 33 | + itype =: 1 NB. index type 1 is tree |
| 34 | + NB. Parse params and update above attributes. |
| 35 | + parse^:(*@#) creation_parameters |
| 36 | + if. 0 <: 4!:0 < 'occupancy' do. |
| 37 | + 13!:8 (3) [ 'Parameter not supported in tree dictionary: occupancy' |
| 38 | + end. |
| 39 | + internal_parameters =. (0 , initsize) ; singlethreaded ; (keytype ; keyshape) ; < (valuetype ; valueshape) |
| 40 | +case. do. |
| 41 | + 13!:8 (3) [ 'Incorrect index type' |
| 42 | +end. |
| 43 | + |
| 44 | +NB. Create the map, which remains as dict. dict is marked nondisplayable because 1 {:: dict is. |
| 45 | +NB. If 1 {:: dict (keys) is an indirect type, it is death to touch or display any part of 1 {:: dict that is on the empty list. |
| 46 | +NB. It might be better not to assign dict, to make it impossible to access 1 {:: dict from console level. But we have to be able to run 16!:_5 on it - make 16!:_5 an adverb |
| 47 | +if. keyhash -: keycompare do. keyfn =. keyhash `: 6 else. keyfn =. keyhash `: 6 : (keycompare `: 6) end. |
| 48 | +size =: initsize |
| 49 | +dict =: keyfn f. (16!:_1) internal_parameters |
| 50 | + |
| 51 | +NB. Assign names. |
| 52 | +if. name -: '' do. prefix =. suffix =. '' else. 'prefix suffix' =. (,{:)&.>/\. split_name name end. |
| 53 | +(prefix , 'get' , suffix) =: dict 16!:_2 |
| 54 | +(prefix , 'put' , suffix) =: dict 16!:_3 |
| 55 | +(prefix , 'del' , suffix) =: dict 16!:_4 |
| 56 | +(prefix , 'has' , suffix) =: dict 16!:_12 |
| 57 | +(prefix , 'len' , suffix) =: 0&(16!:_8)@dict |
| 58 | +if. index_type -: 'tree' do. (prefix , 'getkv' , suffix) =: dict 16!:_6 end. NB. getkv only on rb trees |
| 59 | +EMPTY |
| 60 | +}} |
| 61 | + |
| 62 | +destroy =: {{ |
| 63 | +(1) 16!:_5 dict NB. clear the empty chain in the keys to avoid errors freeing it |
| 64 | +codestroy y NB. destroy the locale, freeing everything |
| 65 | +}} |
| 66 | + |
| 67 | +NB. Resize operation. Nilad. Allocate a larger/smaller dictionary and repopulate its keys |
| 68 | +NB. We have a lock on (dict) during this entire operation |
| 69 | +resize =: {{)m |
| 70 | +size =: SIZE_GROWTH_GEOMETRIC_STEP * size |
| 71 | +NB. We allocate a new DIC block of the correct size. This is a temp whose contents, when filled, will be exchanged into (dict) |
| 72 | +NB. This also allocates new areas for the keys, vals, and hash/tree |
| 73 | +select. itype |
| 74 | +case. 0 do. |
| 75 | + newdict =. dict (16!:_1) 0 , size , <. size * % occupancy NB. allocate new DIC (hashed) |
| 76 | +NB. for hashing: call (newdict 16!:_3) to rehash all the keys. Limit the number of kvs per install to reduce temp space needed. |
| 77 | +NB. Install the kvs from dict into newdict. (e =. 1&(16!:_5) dict) (1) returns the list of empty key indexes; (2) erase the empty chains |
| 78 | +NB. to allow the key block to be freed. Then (<<<e) { keys/vals gives the kvs: |
| 79 | + empties =. (1) 16!:_5 dict NB. get list of empties in dict, then erase the empty chains. Prevents free error when releasing dict |
| 80 | + (newdict 16!:_3)&((<<<empties)&{)&:>/ 2 1 { dict NB. Install all keys from dict into newdict |
| 81 | +case. 1 do. |
| 82 | + newdict =. dict (16!:_1) 0 , size NB. allocate new DIC (tree) |
| 83 | + NB. for red/black: copying the keys, vals, and tree from dict to newdict is done in JE when we return |
| 84 | +end. |
| 85 | +newdict NB. Return the new block. Its contents will be swapped with the old block so that the EPILOG for the resize will free the old keys/vals/hash |
| 86 | +}} |
| 87 | + |
| 88 | +NB. Utils. |
| 89 | +NB. Gives type ID (e.g. 4) from type name (e.g. integer). |
| 90 | +typeid_from_typename =: {{)m |
| 91 | +n =. 1 2 4 8 16 32 64 128 1024 2048 4096 8192 16384 32768 65536 131072 262144 |
| 92 | +n =. n , 5 6 7 9 10 11 |
| 93 | +n =. n , _1 NB. _1 if y is not a name of any type. |
| 94 | +t =. '/boolean/literal/integer/floating/complex/boxed/extended/rational' |
| 95 | +t =. t , '/sparse boolean/sparse literal/sparse integer/sparse floating' |
| 96 | +t =. t , '/sparse complex/sparse boxed/symbol/unicode/unicode4' |
| 97 | +t =. t , '/integer1/integer2/integer4/floating2/floating4/floating16' |
| 98 | +n {~ (<;._1 t) i. < y |
| 99 | +}} |
| 100 | + |
| 101 | +NB. Parse attribute and set its value. |
| 102 | +parse =: {{)m |
| 103 | +'attribute value' =: y |
| 104 | +if. ('literal' -: datatype value) *. (attribute -: 'keytype') +. attribute -: 'valuetype' do. |
| 105 | + value =. typeid_from_typename value |
| 106 | +end. |
| 107 | +(attribute) =: value |
| 108 | +EMPTY |
| 109 | +}}"1 |
| 110 | + |
| 111 | +NB. y is string representing a name with possibly specified locale. Returns two boxes: name ; suffix with locale. |
| 112 | +NB. Right part of hook. |
| 113 | +NB. If '_' is the last character of y then the name has explicitly specified locale, so get the indexes of '_'. |
| 114 | +NB. Left part of hook. |
| 115 | +NB. Use the indexes to split the name or if the number of indexes is less than 2 then suppose name is from base. |
| 116 | +NB. Note that number of indexes may be smaller then number of '_' e.g. 0 when condition from right part of the hook was false. |
| 117 | +split_name =: ('__' ,~&< [)`(({. ,&< }.)~ _2&{)@.(2 <: #@]) ''"_`([: I. '_'&=)@.('_' -: {:) |
0 commit comments