COVERAGE SUMMARY
FILE SUMMARY
NameExecutedRoutines%ExecutedLines%Unexecuted
/home/matt/eu/rds/include/std/map.e2929100.00%54454699.63%2
ROUTINE SUMMARY
RoutineExecutedLinesUnexecuted
put()666897.06%2
calc_hash()33100.00%0
clear()1414100.00%0
compare()1313100.00%0
convert_to_large_map()99100.00%0
convert_to_small_map()88100.00%0
copy()2222100.00%0
for_each()1212100.00%0
get()2121100.00%0
has()66100.00%0
keys()2020100.00%0
load_map()5757100.00%0
map()2525100.00%0
nested_get()99100.00%0
nested_put()77100.00%0
new()1313100.00%0
new_extra()44100.00%0
new_from_kvpairs()77100.00%0
new_from_string()22100.00%0
optimize()1717100.00%0
pairs()2323100.00%0
rehash()3030100.00%0
remove()3232100.00%0
save_map()2525100.00%0
size()22100.00%0
statistics()1818100.00%0
threshold()66100.00%0
type_of()22100.00%0
values()3232100.00%0
LINE COVERAGE DETAIL
#Executed
1
-- (c) Copyright - See License.txt
2
--
3
4
namespace map
5
6
--****
7
-- == Map (hash table)
8
--
9
-- <>
10
--
11
-- A map is a special array, often called an associative array or dictionary,
12
-- in which the index to the data can be any Euphoria object and not just
13
-- an integer. These sort of indexes are also called keys.
14
-- For example we can code things like this...
15
--
16
-- custrec = new() -- Create a new map
17
-- put(custrec, "Name", "Joe Blow")
18
-- put(custrec, "Address", "555 High Street")
19
-- put(custrec, "Phone", 555675632)
20
--
21
-- This creates three elements in the map, and they are indexed by "Name",
22
-- "Address" and "Phone", meaning that to get the data associated with those
23
-- keys we can code ...
24
--
25
-- object data = get(custrec, "Phone")
26
-- -- data now set to 555675632
27
--
28
-- **Note~:** Only one instance of a given key can exist in a given map, meaning
29
-- for example, we couldn't have two separate "Name" values in the above //custrec//
30
-- map.
31
--
32
-- Maps automatically grow to accommodate all the elements placed into it.
33
--
34
-- Associative arrays can be implemented in many different ways, depending
35
-- on what efficiency trade-offs have been made. This implementation allows
36
-- you to decide if you want a //small// map or a //large// map.\\
37
-- ;small map: Faster for small numbers of elements. Speed is usually proportional
38
-- to the number of elements.
39
-- ;large map: Faster for large number of elements. Speed is usually the same
40
-- regardless of how many elements are in the map. The speed is often slower than
41
-- a small map.\\
42
-- **Note~:** If the number of elements placed into a //small// map take it over
43
-- the initial size of the map, it is automatically converted to a //large// map.
44
--
45
46
include std/get.e
47
include std/primes.e
48
include std/convert.e
49
include std/math.e
50
include std/stats.e as stats
51
include std/text.e
52
include std/search.e
53
include std/types.e
54
include std/pretty.e
55
include std/eumem.e
56
include std/error.e
57
include std/sort.e
58
include std/serialize.e
59
include std/datetime.e
60
include std/io.e
61
62
enum
6320
TYPE_TAG, -- ==> 'tag' for map type
6420
ELEMENT_COUNT, -- ==> elementCount
6520
IN_USE, -- ==> count of non-empty buckets
6620
MAP_TYPE, -- ==> Either SMALLMAP or LARGEMAP
6720
KEY_BUCKETS, -- ==> bucket[] --> bucket = {key[], value[]}
6820
VALUE_BUCKETS, -- ==> bucket[] --> bucket = {key[], value[]}
6920
KEY_LIST = 5, -- ==> Small map keys
7020
VALUE_LIST, -- ==> Small map values
7120
FREE_LIST -- ==> Small map freespace
72
7320
constant type_is_map = "Eu:StdMap"
74
75
--****
76
-- Signature:
77
-- function hash(object source, atom algo)
78
--
79
-- Description:
80
-- Calculates a hash value from //key// using the algorithm //algo//
81
--
82
-- Parameters:
83
-- # ##source## : Any Euphoria object
84
-- # ##algo## : A code indicating which algorithm to use.
85
-- ** -5 uses Hsieh. Fastest and good dispersion
86
-- ** -4 uses Fletcher. Very fast and good dispersion
87
-- ** -3 uses Adler. Very fast and reasonable dispersion, especially for small strings
88
-- ** -2 uses MD5 (not implemented yet) Slower but very good dispersion.
89
-- Suitable for signatures.
90
-- ** -1 uses SHA256 (not implemented yet) Slow but excellent dispersion.
91
-- Suitable for signatures. More secure than MD5.
92
-- ** 0 and above (integers and decimals) and non-integers less than zero use
93
-- the cyclic variant (hash = hash * algo + c).
94
-- This is a fast and good to excellent
95
-- dispersion depending on the value of //algo//. Decimals give better dispersion but are
96
-- slightly slower.
97
--
98
-- Returns:
99
-- An **integer**,
100
-- Except for the MD5 and SHA256 algorithms, this is a 32-bit integer.\\
101
-- A **sequence**,
102
-- MD5 returns a 4-element sequence of integers\\
103
-- SHA256 returns a 8-element sequence of integers.
104
--
105
-- Comments:
106
-- * For //algo// values from zero to less than 1, that actual value used is (algo + 69096).
107
--
108
-- Example 1:
109
--
110
-- x = hash("The quick brown fox jumps over the lazy dog", 0)
111
-- -- x is 242399616
112
-- x = hash("The quick brown fox jumps over the lazy dog", 99.94)
113
-- -- x is 723158
114
-- x = hash("The quick brown fox jumps over the lazy dog", -99.94)
115
-- -- x is 4175585990
116
-- x = hash("The quick brown fox jumps over the lazy dog", -4)
117
-- -- x is 467406810
118
--
119
120
--****
121
-- === Hashing Algorithms
122
123
public enum
12420
HSIEH32 = -5,
12520
FLETCHER32,
12620
ADLER32,
12720
MD5,
12820
SHA256
129
130
--****
131
-- === Operation codes for put
132
133
public enum
13420
PUT,
13520
ADD,
13620
SUBTRACT,
13720
MULTIPLY,
13820
DIVIDE,
13920
APPEND,
14020
CONCAT,
14120
LEAVE
142
14320
constant INIT_OPERATIONS = {PUT, APPEND, CONCAT, ADD, SUBTRACT, LEAVE}
144
--****
145
-- === Types of Maps
146
14720
public constant SMALLMAP = 's'
14820
public constant LARGEMAP = 'L'
149
15020
integer threshold_size = 50
151
152
-- This is a improbable value used to initialize a small map's keys list.
15320
constant init_small_map_key = -75960.358941
154
155
--****
156
-- === Types
157
--
158
159
--**
160
-- Defines the datatype 'map'
161
--
162
-- Comments:
163
-- Used when declaring a map variable.
164
--
165
-- Example:
166
--
167
-- map SymbolTable = new() -- Create a new map to hold the symbol table.
168
--
169
1705341
171
-- Must be a valid EuMem pointer.
1725341
if not valid(obj_p, "") then return 0 end if
173
174
-- Large maps have six data elements:
175
-- (1) Data type magic value
176
-- (2) Count of elements
177
-- (3) Number of slots being used
178
-- (4) The map type
179
-- (5) Key Buckets
180
-- (6) Value Buckets
181
-- A bucket contains one or more lists of items.
182
183
-- Small maps have seven data elements:
184
-- (1) Data type magic value
185
-- (2) Count of elements
186
-- (3) Number of slots being used
187
-- (4) The map type
188
-- (5) Sequence of keys
189
-- (6) Sequence of values, in the same order as the keys.
190
-- (7) Sequence. A Free space map.
1915333
object m_
192
1935333
m_ = ram_space[obj_p]
1945333
if not sequence(m_) then return 0 end if
1955331
if length(m_) < 6 then return 0 end if
1965331
if length(m_) > 7 then return 0 end if
1975331
if not equal(m_[TYPE_TAG], type_is_map) then return 0 end if
1985331
if not integer(m_[ELEMENT_COUNT]) then return 0 end if
1995331
if m_[ELEMENT_COUNT] < 0 then return 0 end if
2005331
if not integer(m_[IN_USE]) then return 0 end if
2015331
if m_[IN_USE] < 0 then return 0 end if
2025331
if equal(m_[MAP_TYPE],SMALLMAP) then
203368
if atom(m_[KEY_LIST]) then return 0 end if
204368
if atom(m_[VALUE_LIST]) then return 0 end if
205368
if atom(m_[FREE_LIST]) then return 0 end if
206368
if length(m_[KEY_LIST]) = 0 then return 0 end if
207368
if length(m_[KEY_LIST]) != length(m_[VALUE_LIST]) then return 0 end if
208368
if length(m_[KEY_LIST]) != length(m_[FREE_LIST]) then return 0 end if
2094963
elsif equal(m_[MAP_TYPE],LARGEMAP) then
2104962
if atom(m_[KEY_BUCKETS]) then return 0 end if
2114962
if atom(m_[VALUE_BUCKETS]) then return 0 end if
2124962
if length(m_[KEY_BUCKETS]) != length(m_[VALUE_BUCKETS]) then return 0 end if
213
else
2141
return 0
215
end if
2165330
return 1
217
end type
218
21920
constant maxInt = #3FFFFFFF,
22020
maxAutoSize = floor( (maxInt - 1 ) / 3.5 )
221
222
--****
223
-- === Routines
224
--
225
226
--**
227
-- Calculate a Hashing value from the supplied data.
228
--
229
-- Parameters:
230
-- # ##pData## : The data for which you want a hash value calculated.
231
-- # ##max_hash_p## : The returned value will be no larger than this value.
232
--
233
-- Returns:
234
-- An **integer**, the value of which depends only on the supplied data.
235
--
236
-- Comments:
237
-- This is used whenever you need a single number to represent the data you supply.
238
-- It can calculate the number based on all the data you give it, which can be
239
-- an atom or sequence of any value.
240
--
241
-- Example 1:
242
--
243
-- integer h1
244
-- -- calculate a hash value and ensure it will be a value from 1 to 4097.
245
-- h1 = calc_hash( symbol_name, 4097 )
246
--
247
24844144
249
atom ret_
250
25144144
ret_ = hash(key_p, -4) --HSIEH32)
25244144
return remainder(ret_, max_hash_p) + 1 -- 1-based
253
254
end function
255
256
--**
257
-- Gets or Sets the threshold value that determines at what point a small map
258
-- converts into a large map structure. Initially this has been set to 50,
259
-- meaning that maps up to 50 elements use the //small map// structure.
260
--
261
-- Parameters:
262
-- # ##new_value_p## : If this is greater than zero then it **sets** the threshold
263
-- value.
264
--
265
-- Returns:
266
-- An **integer**, the current value (when ##new_value_p## is less than 1) or the
267
-- old value prior to setting it to ##new_value_p##.
268
--
26916
270
27116
if new_value_p < 1 then
27213
return threshold_size
273
end if
274
2753
integer old_value_ = threshold_size
2763
threshold_size = new_value_p
2773
return old_value_
278
279
end function
280
281
--**
282
-- Determines the type of the map.
283
--
284
-- Parameters:
285
-- # ##m## : A map
286
--
287
-- Returns:
288
-- An **integer**, Either //SMALLMAP// or //LARGEMAP//
289
--
2908
2918
return ram_space[the_map_p][MAP_TYPE]
292
end function
293
294
--**
295
-- Changes the width, i.e. the number of buckets, of a map. Only effects
296
-- //large// maps.
297
--
298
-- Parameters:
299
-- # ##m## : the map to resize
300
-- # ##requested_bucket_size_p## : a lower limit for the new size.
301
--
302
-- Returns:
303
-- A **map**, with the same data in, but more evenly dispatched and hence faster to use.
304
--
305
-- Comment:
306
-- If ##requested_bucket_size_p## is not greater than zero, a new width is automatically derived from the current one.
307
--
308
-- See Also:
309
-- [[:statistics]], [[:optimize]]
310
311
31218
313
integer size_
314
integer index_2_
315
sequence old_key_buckets_
316
sequence old_val_buckets_
317
sequence new_key_buckets_
318
sequence new_val_buckets_
319
object key_
320
object value_
321
sequence temp_map_
322
32318
if ram_space[the_map_p][MAP_TYPE] = SMALLMAP then
3241
return -- small maps are not hashed.
325
end if
326
32717
if requested_bucket_size_p <= 0 then
328
-- grow bucket size_
3291
size_ = floor(length(ram_space[the_map_p][KEY_BUCKETS]) * 3.5) + 1
330
else
33116
size_ = requested_bucket_size_p
332
end if
333
33417
size_ = next_prime(size_, -size_, 2) -- Allow up to 2 seconds to calc next prime.
33517
if size_ < 0 then
3361
return -- don't do anything. New size would take too long.
337
end if
33816
old_key_buckets_ = ram_space[the_map_p][KEY_BUCKETS]
33916
old_val_buckets_ = ram_space[the_map_p][VALUE_BUCKETS]
34016
new_key_buckets_ = repeat({}, size_)
34116
new_val_buckets_ = repeat({}, size_)
34216
temp_map_ = {type_is_map, 0, 0, LARGEMAP}
343
34416
for index = 1 to length(old_key_buckets_) do
34548008
for entry_idx = 1 to length(old_key_buckets_[index]) do
34639374
key_ = old_key_buckets_[index][entry_idx]
34739374
value_ = old_val_buckets_[index][entry_idx]
34839374
index_2_ = calc_hash(key_, size_)
34939374
new_key_buckets_[index_2_] = append(new_key_buckets_[index_2_], key_)
35039374
new_val_buckets_[index_2_] = append(new_val_buckets_[index_2_], value_)
35139374
temp_map_[ELEMENT_COUNT] += 1
35239374
if length(new_key_buckets_[index_2_]) = 1 then
35320627
temp_map_[IN_USE] += 1
354
end if
35539374
end for
35648008
end for
357
35816
temp_map_ = append(temp_map_, new_key_buckets_)
35916
temp_map_ = append(temp_map_, new_val_buckets_)
360
36116
ram_space[the_map_p] = temp_map_
36216
end procedure
363
364
--**
365
-- Create a new map data structure
366
--
367
-- Parameters:
368
-- # ##initial_size_p## : An estimate of how many initial elements will be stored
369
-- in the map. If this value is less than the [[:threshold]] value, the map
370
-- will initially be a //small// map otherwise it will be a //large// map.
371
--
372
-- Returns:
373
-- An empty **map**.
374
--
375
-- Comments:
376
-- A new object of type map is created. The resources allocated for the map will
377
-- be automatically cleaned up if the reference count of the returned value drops
378
-- to zero, or if passed in a call to [[:delete]].
379
--
380
-- Example 1:
381
--
382
-- map m = new() -- m is now an empty map
383
-- map x = new(threshold()) -- Forces a small map to be initialized
384
-- x = new() -- the resources for the map previously stored in x are released automatically
385
-- delete( m ) -- the resources for the map are released
386
--
387
38880
389
integer buckets_
390
sequence new_map_
391
atom temp_map_
392
39380
if initial_size_p < 3 then
3941
initial_size_p = 3
395
end if
39680
if initial_size_p > threshold_size then
397
-- Return a large map
39857
buckets_ = floor(initial_size_p / 30)
39957
if buckets_ < 23 then
4007
buckets_ = 23
401
else
40250
buckets_ = next_prime(buckets_)
403
end if
404
405
40657
new_map_ = {type_is_map, 0, 0, LARGEMAP, repeat({}, buckets_), repeat({}, buckets_)}
407
else
408
-- Return a small map
40923
new_map_ = {type_is_map, 0,0, SMALLMAP, repeat(init_small_map_key, initial_size_p), repeat(0, initial_size_p), repeat(0, initial_size_p)}
410
end if
41180
temp_map_ = malloc()
41280
ram_space[temp_map_] = new_map_
413
41480
return temp_map_
415
end function
416
417
--**
418
-- Returns either the supplied map or a new map.
419
--
420
-- Parameters:
421
-- # ##the_map_p## : An object, that could be an existing map
422
-- # ##initial_size_p## : An estimate of how many initial elements will be stored
423
-- in a new map.
424
--
425
-- Returns:
426
-- A **map**,
427
-- If ##m## is an existing map then it is returned otherwise this
428
-- returns a new empty **map**.
429
--
430
-- Comments:
431
-- This is used to return a new map if the supplied variable isn't already
432
-- a map.
433
--
434
-- Example 1:
435
--
436
-- map m = new_extra( foo() ) -- If foo() returns a map it is used, otherwise
437
-- -- a new map is created.
438
--
439
4408
4418
if map(the_map_p) then
4422
return the_map_p
443
else
4446
return new(initial_size_p)
445
end if
446
end function
447
448
449
--**
450
-- Compares two maps to test equality.
451
--
452
-- Parameters:
453
-- # ##map_1_p## : A map
454
-- # ##map_2_p## : A map
455
-- # ##scope_p## : An integer that specifies what to compare.
456
-- ** 'k' or 'K' to only compare keys.
457
-- ** 'v' or 'V' to only compare values.
458
-- ** 'd' or 'D' to compare both keys and values. This is the default.
459
--
460
-- Returns:
461
-- An **integer**,
462
-- * -1 if they are not equal.
463
-- * 0 if they are literally the same map.
464
-- * 1 if they contain the same keys and/or values.
465
--
466
-- Example 1:
467
--
468
-- map map_1_p = foo()
469
-- map map_2_p = bar()
470
-- if compare(map_1_p, map_2_p, 'k') >= 0 then
471
-- ... -- two maps have the same keys
472
--
473
47418
475
sequence data_set_1_
476
sequence data_set_2_
477
47818
if map_1_p = map_2_p then
4792
return 0
480
end if
481
48216
switch scope_p do
483
case 'v', 'V' then
4843
data_set_1_ = sort(values(map_1_p))
4853
data_set_2_ = sort(values(map_2_p))
486
487
case 'k', 'K' then
4883
data_set_1_ = keys(map_1_p, 1)
4893
data_set_2_ = keys(map_2_p, 1)
490
491
case else
49210
data_set_1_ = pairs(map_1_p, 1)
49310
data_set_2_ = pairs(map_2_p, 1)
494
495
end switch
496
49716
if equal(data_set_1_, data_set_2_) then
49812
return 1
499
end if
500
5014
return -1
502
503
end function
504
505
--**
506
-- Check whether map has a given key.
507
--
508
-- Parameters:
509
-- # ##the_map_p## : the map to inspect
510
-- # ##the_key_p## : an object to be looked up
511
--
512
-- Returns:
513
-- An **integer**, 0 if not present, 1 if present.
514
--
515
-- Example 1:
516
--
517
-- map the_map_p
518
-- the_map_p = new()
519
-- put(the_map_p, "name", "John")
520
-- ? has(the_map_p, "name") -- 1
521
-- ? has(the_map_p, "age") -- 0
522
--
523
--See Also:
524
-- [[:get]]
52517
526
integer index_
527
integer pos_
528
52917
if ram_space[the_map_p][MAP_TYPE] = LARGEMAP then
53012
index_ = calc_hash(the_key_p, length(ram_space[the_map_p][KEY_BUCKETS]))
53112
pos_ = find(the_key_p, ram_space[the_map_p][KEY_BUCKETS][index_])
532
else
5335
pos_ = find(the_key_p, ram_space[the_map_p][KEY_LIST])
534
end if
53517
return (pos_ != 0)
536
537
end function
538
539
--**
540
-- Retrieves the value associated to a key in a map.
541
--
542
-- Parameters:
543
-- # ##the_map_p## : the map to inspect
544
-- # ##the_key_p## : an object, the the_key_p being looked tp
545
-- # ##default_value_p## : an object, a default value returned if ##the_key_p## not found.
546
-- The default is 0.
547
--
548
-- Returns:
549
-- An **object**, the value that corresponds to ##the_key_p## in ##the_map_p##.
550
-- If ##the_key_p## is not in ##the_map_p##, ##default_value_p## is returned instead.
551
--
552
-- Example 1:
553
--
554
-- map ages
555
-- ages = new()
556
-- put(ages, "Andy", 12)
557
-- put(ages, "Budi", 13)
558
--
559
-- integer age
560
-- age = get(ages, "Budi", -1)
561
-- if age = -1 then
562
-- puts(1, "Age unknown")
563
-- else
564
-- printf(1, "The age is %d", age)
565
-- end if
566
--
567
-- See Also:
568
-- [[:has]]
569
570209
571
integer bucket_
572
integer pos_
573
integer from_
574
575209
if ram_space[the_map_p][MAP_TYPE] = LARGEMAP then
576155
bucket_ = calc_hash(the_key_p, length(ram_space[the_map_p][KEY_BUCKETS]))
577155
pos_ = find(the_key_p, ram_space[the_map_p][KEY_BUCKETS][bucket_])
578155
if pos_ > 0 then
579107
return ram_space[the_map_p][VALUE_BUCKETS][bucket_][pos_]
580
end if
58148
return default_value_p
582
else
58354
if equal(the_key_p, init_small_map_key) then
5844
from_ = 1
5854
while from_ > 0 do
586102
pos_ = find_from(the_key_p, ram_space[the_map_p][KEY_LIST], from_)
587102
if pos_ then
588100
if ram_space[the_map_p][FREE_LIST][pos_] = 1 then
5892
return ram_space[the_map_p][VALUE_LIST][pos_]
590
end if
591
else
5922
return default_value_p
593
end if
59498
from_ = pos_ + 1
59598
end while
596
else
59750
pos_ = find(the_key_p, ram_space[the_map_p][KEY_LIST])
59850
if pos_ then
59942
return ram_space[the_map_p][VALUE_LIST][pos_]
600
end if
601
end if
602
end if
6038
return default_value_p
604
end function
605
606
--**
607
-- Returns the value that corresponds to the object ##the_keys_p## in the nested map the_map_p. ##the_keys_p## is a
608
-- sequence of keys. If any key is not in the map, the object default_value_p is returned instead.
6095
6105
for i = 1 to length( the_keys_p ) - 1 do
6117
object val_ = get( the_map_p, the_keys_p[1], 0 )
612
6137
if not map( val_ ) then
614
-- not a map
6151
return default_value_p
616
else
6176
the_map_p = val_
6186
the_keys_p = the_keys_p[2..$]
619
end if
6206
end for
6214
return get( the_map_p, the_keys_p[1], default_value_p )
622
end function
623
624
--**
625
-- Adds or updates an entry on a map.
626
--
627
-- Parameters:
628
-- # ##the_map_p## : the map where an entry is being added or updated
629
-- # ##the_key_p## : an object, the the_key_p to look up
630
-- # ##the_value_p## : an object, the value to add, or to use for updating.
631
-- # ##operation## : an integer, indicating what is to be done with ##the_value_p##. Defaults to PUT.
632
-- # ##trigger_p## : an integer. Default is 100. See Comments for details.
633
--
634
-- Returns:
635
-- The updated **map**.
636
--
637
-- Comments:
638
-- * The operation parameter can be used to modify the existing value. Valid operations are:
639
--
640
-- ** ##PUT## ~-- This is the default, and it replaces any value in there already
641
-- ** ##ADD## ~-- Equivalent to using the += operator
642
-- ** ##SUBTRACT## ~-- Equivalent to using the -= operator
643
-- ** ##MULTIPLY## ~-- Equivalent to using the *= operator
644
-- ** ##DIVIDE## ~-- Equivalent to using the /= operator
645
-- ** ##APPEND## ~-- Appends the value to the existing data
646
-- ** ##CONCAT## ~-- Equivalent to using the &= operator
647
-- ** ##LEAVE## ~-- If it already exists, the current value is left unchanged
648
-- otherwise the new value is added to the map.
649
--
650
-- * The //trigger// parameter is used when you need to keep the average
651
-- number of keys in a hash bucket to a specific maximum. The //trigger//
652
-- value is the maximum allowed. Each time a //put// operation increases
653
-- the hash table's average bucket size to be more than the //trigger// value
654
-- the table is expanded by a factor of 3.5 and the keys are rehashed into the
655
-- enlarged table. This can be a time intensive action so set the value
656
-- to one that is appropriate to your application.
657
-- ** By keeping the average bucket size to a certain maximum, it can
658
-- speed up lookup times.
659
-- ** If you set the //trigger// to zero, it will not check to see if
660
-- the table needs reorganizing. You might do this if you created the original
661
-- bucket size to an optimal value. See [[:new]] on how to do this.
662
--
663
-- Example 1:
664
--
665
-- map ages
666
-- ages = new()
667
-- put(ages, "Andy", 12)
668
-- put(ages, "Budi", 13)
669
-- put(ages, "Budi", 14)
670
--
671
-- -- ages now contains 2 entries: "Andy" => 12, "Budi" => 14
672
--
673
--
674
-- See Also:
675
-- [[:remove]], [[:has]], [[:nested_put]]
676
6774788
678
integer index_
679
integer bucket_
680
atom average_length_
681
integer from_
682
6834788
if ram_space[the_map_p][MAP_TYPE] = LARGEMAP then
6844599
bucket_ = calc_hash(the_key_p, length(ram_space[the_map_p][KEY_BUCKETS]))
6854599
index_ = find(the_key_p, ram_space[the_map_p][KEY_BUCKETS][bucket_])
6864599
if index_ > 0 then
687
-- The the_value_p already exists.
68826
switch operation_p do
689
case PUT then
6904
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] = the_value_p
691
692
case ADD then
6932
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] += the_value_p
694
695
case SUBTRACT then
6961
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] -= the_value_p
697
698
case MULTIPLY then
6991
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] *= the_value_p
700
701
case DIVIDE then
7021
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] /= the_value_p
703
704
case APPEND then
70514
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] = append( ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_], the_value_p )
706
707
case CONCAT then
7081
ram_space[the_map_p][VALUE_BUCKETS][bucket_][index_] &= the_value_p
709
710
case LEAVE then
711
-- Do nothing
7121
operation_p = operation_p
713
714
case else
7151
crash("Unknown operation given to map.e:put()")
716
717
end switch
71825
return
719
end if
720
7214573
if not eu:find(operation_p, INIT_OPERATIONS) then
7221
crash("Inappropriate initial operation given to map.e:put()")
723
end if
7244572
if operation_p = LEAVE then
7251
return
726
end if
727
728
729
-- write new entry
7304571
if operation_p = APPEND then
731
-- If appending, then the user wants the the_value_p to be an element, not the entire thing
7322
the_value_p = { the_value_p }
733
end if
734
7354571
ram_space[the_map_p][IN_USE] += (length(ram_space[the_map_p][KEY_BUCKETS][bucket_]) = 0)
7364571
ram_space[the_map_p][ELEMENT_COUNT] += 1 -- elementCount
7374571
ram_space[the_map_p][KEY_BUCKETS][bucket_] = append(ram_space[the_map_p][KEY_BUCKETS][bucket_], the_key_p)
7384571
ram_space[the_map_p][VALUE_BUCKETS][bucket_] = append(ram_space[the_map_p][VALUE_BUCKETS][bucket_], the_value_p)
739
7404571
if trigger_p > 0 then
7414315
average_length_ = ram_space[the_map_p][ELEMENT_COUNT] / ram_space[the_map_p][IN_USE]
7424315
if (average_length_ >= trigger_p) then
7431
rehash(the_map_p)
744
end if
745
end if
746
7474571
return
748
else -- Small Map
749
-- First, check to see if the key is already in the map.
750189
if equal(the_key_p, init_small_map_key) then
751
-- Special case when the key happens to be the magic init value.
752
-- We have to double check the free list whenever we have a hit on the key.
7532
from_ = 1
7542
while index_ > 0 with entry do
75551
if ram_space[the_map_p][FREE_LIST][index_] = 1 then
7561
exit
757
end if
75850
from_ = index_ + 1
759
entry
76052
index_ = find_from(the_key_p, ram_space[the_map_p][KEY_LIST], from_)
76152
end while
762
else
763187
index_ = find(the_key_p, ram_space[the_map_p][KEY_LIST])
764
end if
765
766
-- Did we find the key?
767189
if index_ = 0 then
768
769166
if not eu:find(operation_p, INIT_OPERATIONS) then
7701
crash("Inappropriate initial operation given to map.e:put()")
771
end if
772
-- No, so add it.
773165
index_ = find(0, ram_space[the_map_p][FREE_LIST])
774165
if index_ = 0 then
775
-- No room left, so now it becomes a large map.
7761
convert_to_large_map(the_map_p)
7771
put(the_map_p, the_key_p, the_value_p, operation_p, trigger_p)
7781
return
779
end if
780
781164
ram_space[the_map_p][KEY_LIST][index_] = the_key_p
782164
ram_space[the_map_p][FREE_LIST][index_] = 1
783164
ram_space[the_map_p][IN_USE] += 1
784164
ram_space[the_map_p][ELEMENT_COUNT] += 1
785
786164
if operation_p = APPEND then
7871
the_value_p = { the_value_p }
788
end if
789164
if operation_p != LEAVE then
790164
operation_p = PUT -- Initially, nearly everything is a PUT.
791
end if
792
end if
793
794187
switch operation_p do
795
case PUT then
796170
ram_space[the_map_p][VALUE_LIST][index_] = the_value_p
797
798
case ADD then
7991
ram_space[the_map_p][VALUE_LIST][index_] += the_value_p
800
801
case SUBTRACT then
8021
ram_space[the_map_p][VALUE_LIST][index_] -= the_value_p
803
804
case MULTIPLY then
8051
ram_space[the_map_p][VALUE_LIST][index_] *= the_value_p
806
807
case DIVIDE then
8081
ram_space[the_map_p][VALUE_LIST][index_] /= the_value_p
809
810
case APPEND then
81111
ram_space[the_map_p][VALUE_LIST][index_] = append( ram_space[the_map_p][VALUE_LIST][index_], the_value_p )
812
813
case CONCAT then
8141
ram_space[the_map_p][VALUE_LIST][index_] &= the_value_p
815
816
case LEAVE then
817
-- do nothing
8180
case else
8191
crash("Unknown operation given to map.e:put()")
820
821
end switch
822186
return
823
824
end if
8250
end procedure
826
827
828
--**
829
-- Adds or updates an entry on a map.
830
--
831
-- Parameters:
832
-- # ##the_map_p## : the map where an entry is being added or updated
833
-- # ##the_keys_p## : a sequence of keys for the nested maps
834
-- # ##the_value_p## : an object, the value to add, or to use for updating.
835
-- # ##operation_p## : an integer, indicating what is to be done with ##value##. Defaults to PUT.
836
-- # ##trigger_p## : an integer. Default is 51. See Comments for details.
837
--
838
-- Valid operations are:
839
--
840
-- * ##PUT## ~-- This is the default, and it replaces any value in there already
841
-- * ##ADD## ~-- Equivalent to using the += operator
842
-- * ##SUBTRACT## ~-- Equivalent to using the -= operator
843
-- * ##MULTIPLY## ~-- Equivalent to using the *= operator
844
-- * ##DIVIDE## ~-- Equivalent to using the /= operator
845
-- * ##APPEND## ~-- Appends the value to the existing data
846
-- * ##CONCAT## ~-- Equivalent to using the &= operator
847
--
848
-- Returns:
849
-- The modified **map**.
850
--
851
-- Comments:
852
-- * If existing entry with the same key is already in the map, the value of the entry is updated.
853
-- * The //trigger// parameter is used when you need to keep the average
854
-- number of keys in a hash bucket to a specific maximum. The //trigger//
855
-- value is the maximum allowed. Each time a //put// operation increases
856
-- the hash table's average bucket size to be more than the //trigger// value
857
-- the table is expanded by a factor 3.5 and the keys are rehashed into the
858
-- enlarged table. This can be a time intensive action so set the value
859
-- to one that is appropriate to your application.
860
-- ** By keeping the average bucket size to a certain maximum, it can
861
-- speed up lookup times.
862
-- ** If you set the //trigger// to zero, it will not check to see if
863
-- the table needs reorganizing. You might do this if you created the original
864
-- bucket size to an optimal value. See [[:new]] on how to do this.
865
--
866
-- Example 1:
867
--
868
-- map city_population
869
-- city_population = new()
870
-- nested_put(city_population, {"United States", "California", "Los Angeles"}, 3819951 )
871
-- nested_put(city_population, {"Canada", "Ontario", "Toronto"}, 2503281 )
872
--
873
--
874
-- See also: [[:put]]
8759
876
atom temp_map_
877
8789
if length( the_keys_p ) = 1 then
8793
put( the_map_p, the_keys_p[1], the_value_p, operation_p, trigger_p )
880
else
8816
temp_map_ = new_extra( get( the_map_p, the_keys_p[1] ) )
8826
nested_put( temp_map_, the_keys_p[2..$], the_value_p, operation_p, trigger_p )
8836
put( the_map_p, the_keys_p[1], temp_map_, PUT, trigger_p )
884
end if
8859
end procedure
886
887
--**
888
-- Remove an entry with given key from a map.
889
--
890
-- Parameters:
891
-- # ##the_map_p## : the map to operate on
892
-- # ##key## : an object, the key to remove.
893
--
894
-- Returns:
895
-- The modified **map**.
896
--
897
-- Comments:
898
-- * If ##key## is not on ##the_map_p##, the ##the_map_p## is returned unchanged.
899
-- * If you need to remove all entries, see [[:clear]]
900
--
901
-- Example 1:
902
--
903
-- map the_map_p
904
-- the_map_p = new()
905
-- put(the_map_p, "Amy", 66.9)
906
-- remove(the_map_p, "Amy")
907
-- -- the_map_p is now an empty map again
908
--
909
--
910
-- See Also:
911
-- [[:clear]], [[:has]]
912
9136
914
integer index_
915
integer bucket_
916
sequence temp_map_
917
integer from_
918
919
9206
temp_map_ = ram_space[the_map_p]
9216
if temp_map_[MAP_TYPE] = LARGEMAP then
9224
bucket_ = calc_hash(the_key_p, length(temp_map_[KEY_BUCKETS]))
923
9244
index_ = find(the_key_p, temp_map_[KEY_BUCKETS][bucket_])
9254
if index_ != 0 then
9264
temp_map_[ELEMENT_COUNT] -= 1
9274
if length(temp_map_[KEY_BUCKETS][bucket_]) = 1 then
9283
temp_map_[IN_USE] -= 1
9293
temp_map_[KEY_BUCKETS][bucket_] = {}
9303
temp_map_[VALUE_BUCKETS][bucket_] = {}
931
else
9321
temp_map_[VALUE_BUCKETS][bucket_] = temp_map_[VALUE_BUCKETS][bucket_][1 .. index_-1] & temp_map_[VALUE_BUCKETS][bucket_][index_+1 .. $]
9331
temp_map_[KEY_BUCKETS][bucket_] = temp_map_[KEY_BUCKETS][bucket_][1 .. index_-1] & temp_map_[KEY_BUCKETS][bucket_][index_+1 .. $]
934
end if
935
9364
if temp_map_[ELEMENT_COUNT] < floor(51 * threshold_size / 100) then
9373
ram_space[the_map_p] = temp_map_
9383
convert_to_small_map(the_map_p)
9393
return
940
end if
941
end if
942
else
9432
from_ = 1
9442
while from_ > 0 do
9453
index_ = find_from(the_key_p, temp_map_[KEY_LIST], from_)
9463
if index_ then
9471
if temp_map_[FREE_LIST][index_] = 1 then
9481
temp_map_[FREE_LIST][index_] = 0
9491
temp_map_[KEY_LIST][index_] = init_small_map_key
9501
temp_map_[VALUE_LIST][index_] = 0
9511
temp_map_[IN_USE] -= 1
9521
temp_map_[ELEMENT_COUNT] -= 1
953
end if
954
else
9552
exit
956
end if
9571
from_ = index_ + 1
9581
end while
959
end if
9603
ram_space[the_map_p] = temp_map_
9613
end procedure
962
963
--**
964
-- Remove all entries in a map.
965
--
966
-- Parameters:
967
-- # ##the_map_p## : the map to operate on
968
--
969
-- Comments:
970
-- * This is much faster than removing each entry individually.
971
-- * If you need to remove just one entry, see [[:remove]]
972
--
973
-- Example 1:
974
--
975
-- map the_map_p
976
-- the_map_p = new()
977
-- put(the_map_p, "Amy", 66.9)
978
-- put(the_map_p, "Betty", 67.8)
979
-- put(the_map_p, "Claire", 64.1)
980
-- ...
981
-- clear(the_map_p)
982
-- -- the_map_p is now an empty map again
983
--
984
--
985
-- See Also:
986
-- [[:remove]], [[:has]]
987
98810
989
sequence temp_map_
990
99110
temp_map_ = ram_space[the_map_p]
99210
if temp_map_[MAP_TYPE] = LARGEMAP then
9936
temp_map_[ELEMENT_COUNT] = 0
9946
temp_map_[IN_USE] = 0
9956
temp_map_[KEY_BUCKETS] = repeat({}, length(temp_map_[KEY_BUCKETS]))
9966
temp_map_[VALUE_BUCKETS] = repeat({}, length(temp_map_[VALUE_BUCKETS]))
997
else
9984
temp_map_[ELEMENT_COUNT] = 0
9994
temp_map_[IN_USE] = 0
10004
temp_map_[KEY_LIST] = repeat(init_small_map_key, length(temp_map_[KEY_LIST]))
10014
temp_map_[VALUE_LIST] = repeat(0, length(temp_map_[VALUE_LIST]))
10024
temp_map_[FREE_LIST] = repeat(0, length(temp_map_[FREE_LIST]))
1003
end if
100410
ram_space[the_map_p] = temp_map_
100510
end procedure
1006
1007
--**
1008
-- Return the number of entries in a map.
1009
--
1010
-- Parameters:
1011
-- ##the_map_p## : the map being queried
1012
--
1013
-- Returns:
1014
-- An **integer**, the number of entries it has.
1015
--
1016
-- Comments:
1017
-- For an empty map, size will be zero
1018
--
1019
-- Example 1:
1020
--
1021
-- map the_map_p
1022
-- put(the_map_p, 1, "a")
1023
-- put(the_map_p, 2, "b")
1024
-- ? size(the_map_p) -- outputs 2
1025
--
1026
--
1027
-- See Also:
1028
-- [[:statistics]]
102910
103010
return ram_space[the_map_p][ELEMENT_COUNT]
1031
end function
1032
1033
--**
1034
-- Retrieves characteristics of a map.
1035
--
1036
-- Parameters:
1037
-- # ##the_map_p## : the map being queried
1038
--
1039
-- Returns:
1040
-- A **sequence**, of 7 integers:
1041
-- * ##NUM_ENTRIES## ~-- number of entries
1042
-- * ##NUM_IN_USE## ~-- number of buckets in use
1043
-- * ##NUM_BUCKETS## ~-- number of buckets
1044
-- * ##LARGEST_BUCKET## ~-- size of largest bucket
1045
-- * ##SMALLEST_BUCKET## ~-- size of smallest bucket
1046
-- * ##AVERAGE_BUCKET## ~-- average size for a bucket
1047
-- * ##STDEV_BUCKET## ~-- standard deviation for the bucket length series
1048
--
1049
-- Example 1:
1050
--
1051
-- sequence s = statistics(mymap)
1052
-- printf(1, "The average size of the buckets is %d", s[AVERAGE_BUCKET])
1053
--
1054
--
1055
1056
public enum
105720
NUM_ENTRIES,
105820
NUM_IN_USE,
105920
NUM_BUCKETS,
106020
LARGEST_BUCKET,
106120
SMALLEST_BUCKET,
106220
AVERAGE_BUCKET,
106320
STDEV_BUCKET
1064
106520
1066
sequence statistic_set_
1067
sequence lengths_
1068
integer length_
1069
sequence temp_map_
1070
107120
temp_map_ = ram_space[the_map_p]
1072
107320
if temp_map_[MAP_TYPE] = LARGEMAP then
107418
statistic_set_ = {temp_map_[ELEMENT_COUNT], temp_map_[IN_USE], length(temp_map_[KEY_BUCKETS]), 0, maxInt, 0, 0}
107518
lengths_ = {}
107618
for i = 1 to length(temp_map_[KEY_BUCKETS]) do
107758813
length_ = length(temp_map_[KEY_BUCKETS][i])
107858813
if length_ > 0 then
107923431
if length_ > statistic_set_[LARGEST_BUCKET] then
108060
statistic_set_[LARGEST_BUCKET] = length_
1081
end if
108223431
if length_ < statistic_set_[SMALLEST_BUCKET] then
108340
statistic_set_[SMALLEST_BUCKET] = length_
1084
end if
108523431
lengths_ &= length_
1086
end if
108758813
end for
108818
statistic_set_[AVERAGE_BUCKET] = stats:average(lengths_)
108918
statistic_set_[STDEV_BUCKET] = stats:stdev(lengths_)
1090
else
10912
statistic_set_ = {temp_map_[ELEMENT_COUNT],
1092
temp_map_[IN_USE],
1093
length(temp_map_[KEY_LIST]),
1094
length(temp_map_[KEY_LIST]),
1095
length(temp_map_[KEY_LIST]),
1096
length(temp_map_[KEY_LIST]),
1097
0}
1098
end if
109920
return statistic_set_
1100
end function
1101
1102
--**
1103
-- Return all keys in a map.
1104
--
1105
-- Parameters;
1106
-- # ##the_map_p##: the map being queried
1107
-- # ##sorted_result##: optional integer. 0 [default] means do not sort the
1108
-- output and 1 means to sort the output before returning.
1109
--
1110
-- Returns:
1111
-- A **sequence** made of all the keys in the map.
1112
--
1113
-- Comments:
1114
-- If ##sorted_result## is not used, the order of the keys returned is not predicable.
1115
--
1116
-- Example 1:
1117
--
1118
-- map the_map_p
1119
-- the_map_p = new()
1120
-- put(the_map_p, 10, "ten")
1121
-- put(the_map_p, 20, "twenty")
1122
-- put(the_map_p, 30, "thirty")
1123
-- put(the_map_p, 40, "forty")
1124
--
1125
-- sequence keys
1126
-- keys = keys(the_map_p) -- keys might be {20,40,10,30} or some other order
1127
-- keys = keys(the_map_p, 1) -- keys will be {10,20,30,40}
1128
--
1129
--
1130
-- See Also:
1131
-- [[:has]], [[:values]], [[:pairs]]
1132
--
113314
1134
sequence buckets_
1135
sequence current_bucket_
1136
sequence results_
1137
integer pos_
1138
sequence temp_map_
1139
114014
temp_map_ = ram_space[the_map_p]
1141
114214
results_ = repeat(0, temp_map_[ELEMENT_COUNT])
114314
pos_ = 1
1144
114514
if temp_map_[MAP_TYPE] = LARGEMAP then
11467
buckets_ = temp_map_[KEY_BUCKETS]
11477
for index = 1 to length(buckets_) do
1148209
current_bucket_ = buckets_[index]
1149209
if length(current_bucket_) > 0 then
115022
results_[pos_ .. pos_ + length(current_bucket_) - 1] = current_bucket_
115122
pos_ += length(current_bucket_)
1152
end if
1153209
end for
1154
else
11557
for index = 1 to length(temp_map_[FREE_LIST]) do
1156331
if temp_map_[FREE_LIST][index] != 0 then
115775
results_[pos_] = temp_map_[KEY_LIST][index]
115875
pos_ += 1
1159
end if
1160331
end for
1161
1162
end if
116314
if sorted_result then
11648
return sort(results_)
1165
else
11666
return results_
1167
end if
1168
end function
1169
1170
--**
1171
-- Return values, without their keys, from a map.
1172
--
1173
-- Parameters:
1174
-- # ##the_map## : the map being queried
1175
-- # ##keys## : optional, key list of values to return.
1176
-- # ##default_values## : optional default values for keys list
1177
--
1178
-- Returns:
1179
-- A **sequence**, of all values stored in ##the_map##.
1180
--
1181
-- Comments:
1182
-- * The order of the values returned may not be the same as the putting order.
1183
-- * Duplicate values are not removed.
1184
-- * You use the ##keys## parameter to return a specific set of values from
1185
-- the map. They are returned in the same order as the ##keys## parameter. If
1186
-- no ##default_values## is given and one is needed, 0 will be used.
1187
-- * If ##default_values## is an atom, it represents the default value for all
1188
-- values in ##keys##.
1189
-- * If ##default_values## is a sequence, and its length is less than ##keys##,
1190
-- then the last item in ##default_values## is used for the rest of the ##keys##.
1191
--
1192
-- Example 1:
1193
--
1194
-- map the_map_p
1195
-- the_map_p = new()
1196
-- put(the_map_p, 10, "ten")
1197
-- put(the_map_p, 20, "twenty")
1198
-- put(the_map_p, 30, "thirty")
1199
-- put(the_map_p, 40, "forty")
1200
--
1201
-- sequence values
1202
-- values = values(the_map_p)
1203
-- -- values might be {"twenty","forty","ten","thirty"}
1204
-- -- or some other order
1205
--
1206
--
1207
-- Example 2:
1208
--
1209
-- map the_map_p
1210
-- the_map_p = new()
1211
-- put(the_map_p, 10, "ten")
1212
-- put(the_map_p, 20, "twenty")
1213
-- put(the_map_p, 30, "thirty")
1214
-- put(the_map_p, 40, "forty")
1215
--
1216
-- sequence values
1217
-- values = values(the_map_p, { 10, 50, 30, 9000 })
1218
-- -- values WILL be { "ten", 0, "thirty", 0 }
1219
-- values = values(the_map_p, { 10, 50, 30, 9000 }, {-1,-2,-3,-4})
1220
-- -- values WILL be { "ten", -2, "thirty", -4 }
1221
--
1222
--
1223
-- See Also:
1224
-- [[:get]], [[:keys]], [[:pairs]]
1225
122618
1227
122818
if sequence(keys) then
12295
if atom(default_values) then
12303
default_values = repeat(default_values, length(keys))
12312
elsif length(default_values) < length(keys) then
12321
default_values &= repeat(default_values[$], length(keys) - length(default_values))
1233
end if
1234
12355
for i = 1 to length(keys) do
123617
keys[i] = get(the_map, keys[i], default_values[i])
123717
end for
1238
12395
return keys
1240
end if
1241
124213
sequence buckets_
124313
sequence bucket_
124413
sequence results_
124513
integer pos_
124613
sequence temp_map_
1247
124813
temp_map_ = ram_space[the_map]
1249
125013
results_ = repeat(0, temp_map_[ELEMENT_COUNT])
125113
pos_ = 1
1252
125313
if temp_map_[MAP_TYPE] = LARGEMAP then
12546
buckets_ = temp_map_[VALUE_BUCKETS]
12556
for index = 1 to length(buckets_) do
1256186
bucket_ = buckets_[index]
1257186
if length(bucket_) > 0 then
125820
results_[pos_ .. pos_ + length(bucket_) - 1] = bucket_
125920
pos_ += length(bucket_)
1260
end if
1261186
end for
1262
else
12637
for index = 1 to length(temp_map_[FREE_LIST]) do
1264380
if temp_map_[FREE_LIST][index] != 0 then
1265104
results_[pos_] = temp_map_[VALUE_LIST][index]
1266104
pos_ += 1
1267
end if
1268380
end for
1269
1270
end if
1271
127213
return results_
1273
end function
1274
1275
--**
1276
--
1277
-- Return all key/value pairs in a map.
1278
--
1279
-- Parameters:
1280
-- # ##the_map_p## : the map to get the data from
1281
-- # ##sorted_result## : optional integer. 0 [default] means do not sort the
1282
-- output and 1 means to sort the output before returning.
1283
--
1284
-- Returns:
1285
-- A **sequence**, of all key/value pairs stored in ##the_map_p##. Each pair is a
1286
-- sub-sequence in the form {key, value}
1287
--
1288
-- Comments:
1289
-- If ##sorted_result## is not used, the order of the values returned is not predicable.
1290
--
1291
-- Example 1:
1292
--
1293
-- map the_map_p
1294
-- the_map_p = new()
1295
-- put(the_map_p, 10, "ten")
1296
-- put(the_map_p, 20, "twenty")
1297
-- put(the_map_p, 30, "thirty")
1298
-- put(the_map_p, 40, "forty")
1299
--
1300
-- sequence keyvals
1301
-- keyvals = pairs(the_map_p) -- might be {{20,"twenty"},{40,"forty"},{10,"ten"},{30,"thirty"}}
1302
-- keyvals = pairs(the_map_p, 1) -- will be {{10,"ten"},{20,"twenty"},{30,"thirty"},{40,"forty"}}
1303
--
1304
--
1305
-- See Also:
1306
-- [[:get]], [[:keys]], [[:values]]
1307
--
130827
1309
sequence key_bucket_
1310
sequence value_bucket_
1311
sequence results_
1312
integer pos_
1313
sequence temp_map_
1314
131527
temp_map_ = ram_space[the_map_p]
1316
131727
results_ = repeat({0,0}, temp_map_[ELEMENT_COUNT])
131827
pos_ = 1
1319
132027
if temp_map_[MAP_TYPE] = LARGEMAP then
132115
for index = 1 to length(temp_map_[KEY_BUCKETS]) do
1322441
key_bucket_ = temp_map_[KEY_BUCKETS][index]
1323441
value_bucket_ = temp_map_[VALUE_BUCKETS][index]
1324441
for j = 1 to length(key_bucket_) do
13256008
results_[pos_][1] = key_bucket_[j]
13266008
results_[pos_][2] = value_bucket_[j]
13276008
pos_ += 1
13286008
end for
1329441
end for
1330
else
133112
for index = 1 to length(temp_map_[FREE_LIST]) do
1332516
if temp_map_[FREE_LIST][index] != 0 then
133388
results_[pos_][1] = temp_map_[KEY_LIST][index]
133488
results_[pos_][2] = temp_map_[VALUE_LIST][index]
133588
pos_ += 1
1336
end if
1337516
end for
1338
1339
end if
134027
if sorted_result then
134125
return sort(results_)
1342
else
13432
return results_
1344
end if
1345
end function
1346
1347
--**
1348
-- Widens a map to increase performance.
1349
--
1350
-- Parameters:
1351
-- # ##the_map_p## : the map being optimized
1352
-- # ##max_p## : an integer, the maximum desired size of a bucket. Default is 25.
1353
-- This must be 3 or higher.
1354
-- # ##grow_p## : an atom, the factor to grow the number of buckets for each
1355
-- iteration of rehashing. Default is 1.333. This must be
1356
-- greater than 1.
1357
--
1358
-- Returns:
1359
-- The optimized **map**.
1360
--
1361
-- Comments:
1362
-- This rehashes the map until either the maximum bucket size is less than
1363
-- the desired maximum or the maximum bucket size is less than the largest
1364
-- size statistically expected (mean + 3 standard deviations).
1365
--
1366
-- See Also:
1367
-- [[:statistics]], [[:rehash]]
1368
--
136910
1370
sequence stats_
1371
integer next_guess_
1372
137310
if ram_space[the_map_p][MAP_TYPE] = LARGEMAP then
13745
if grow_p < 1 then
13752
grow_p = 1.333
1376
end if
13775
if max_p < 3 then
13782
max_p = 3
1379
end if
1380
13815
next_guess_ = max({1, floor(ram_space[the_map_p][ELEMENT_COUNT] / max_p)})
13825
while 1 with entry do
1383
138413
if stats_[LARGEST_BUCKET] <= max_p then
13854
exit -- Largest is now smaller than the maximum I wanted.
1386
end if
1387
13889
if stats_[LARGEST_BUCKET] <= (stats_[STDEV_BUCKET]*3 + stats_[AVERAGE_BUCKET]) then
13891
exit -- Largest is smaller than is statistically expected.
1390
end if
1391
13928
next_guess_ = floor(stats_[NUM_BUCKETS] * grow_p)
1393
1394
entry
139513
rehash(the_map_p, next_guess_)
139613
stats_ = statistics(the_map_p)
139713
end while
1398
end if
139910
end procedure
1400
1401
--**
1402
-- Loads a map from a file
1403
--
1404
-- Parameters:
1405
-- # ##file_name_p## : The file to load from. This file may have been created
1406
-- by the [[:save_map]] function. This can either be a
1407
-- name of a file or an already opened file handle.
1408
--
1409
-- Returns:
1410
-- Either a **map**, with all the entries found in ##file_name_p##, or **-1**
1411
-- if the file failed to open, or **-2** if the file is incorrectly formatted.
1412
--
1413
-- Comments:
1414
-- If ##file_name_p## is an already opened file handle, this routine will write
1415
-- to that file and not close it. Otherwise, the named file will be created and
1416
-- closed by this routine.
1417
--
1418
-- The input file can be either one created by the [[:save_map]] function or
1419
-- a manually created/edited text file. See [[:save_map]] for details about
1420
-- the required layout of the text file.
1421
--
1422
--
1423
-- Example 1:
1424
--
1425
-- object loaded
1426
-- map AppOptions
1427
-- sequence SavedMap = "c:\myapp\options.txt"
1428
-- loaded = load_map(SavedMap)
1429
-- if equal(loaded, -1) then
1430
-- crash("Map '%s' failed to open", SavedMap)
1431
-- end if
1432
-- -- By now we know that it was loaded and a new map created,
1433
-- -- so we can assign it to a 'map' variable.
1434
-- AppOptions = loaded
1435
-- if get(AppOptions, "verbose", 1) = 3 then
1436
-- ShowIntructions()
1437
-- end if
1438
--
1439
--
1440
-- See Also:
1441
-- [[:new]], [[:save_map]]
1442
--
14438
1444
integer file_handle_
1445
object line_
1446
integer comment_
1447
integer delim_
1448
object value_
1449
object key_
1450
sequence conv_res_
1451
atom new_map_
1452
14538
if sequence(file_name_p) then
14547
file_handle_ = open(file_name_p, "rb")
1455
else
14561
file_handle_ = file_name_p
1457
end if
14588
if file_handle_ = -1 then
14591
return -1
1460
end if
1461
14627
new_map_ = new(threshold_size) -- Assume a small map initially.
1463
1464
-- Look for a non-printable byte in the first 10 bytes. If none are found then this is a text-formated
1465
-- file otherwise it is a 'raw' saved file.
1466
14677
for i = 1 to 10 do
146832
delim_ = getc(file_handle_)
146932
if delim_ = -1 then
14701
exit
1471
end if
147231
if not t_print(delim_) then
14736
if not t_space(delim_) then
14744
exit
1475
end if
1476
end if
147727
delim_ = -1
147827
end for
1479
14807
if delim_ = -1 then
1481
-- A text format file
14823
close(file_handle_)
14833
file_handle_ = open(file_name_p, "r")
14843
while sequence(line_) with entry do
148532
comment_ = rmatch("--", line_)
148632
if comment_ != 0 then
14873
line_ = trim(line_[1..comment_-1])
1488
end if
148932
delim_ = find('=', line_)
149032
if delim_ > 0 then
149122
key_ = trim(line_[1..delim_-1])
149222
if length(key_) > 0 then
149322
key_ = match_replace("\\-", key_, "-")
149422
if not t_alpha(key_[1]) then
149513
conv_res_ = value(key_,,GET_LONG_ANSWER)
149613
if conv_res_[1] = GET_SUCCESS then
149713
if conv_res_[3] = length(key_) then
149813
key_ = conv_res_[2]
1499
end if
1500
end if
1501
end if
1502
150322
value_ = trim(line_[delim_+1..$])
150422
value_ = match_replace("\\-", value_, "-")
150522
conv_res_ = value(value_,,GET_LONG_ANSWER)
150622
if conv_res_[1] = GET_SUCCESS then
150716
if conv_res_[3] = length(value_) then
150816
value_ = conv_res_[2]
1509
end if
1510
end if
151122
put(new_map_, key_, value_)
1512
end if
1513
end if
1514
entry
151535
line_ = gets(file_handle_)
151635
end while
1517
else
15184
object _ = seek(file_handle_, 0)
15194
line_ = deserialize(file_handle_)
15204
if atom(line_) then
1521
-- failed to decode the file.
15221
return -2
1523
end if
15243
if line_[1] = 1 then
1525
-- Saved Map Format version 1
15262
key_ = deserialize(file_handle_)
15272
value_ = deserialize(file_handle_)
1528
15292
for i = 1 to length(key_) do
153024
put(new_map_, key_[i], value_[i])
153124
end for
1532
else
1533
-- Bad file format
15341
return -2
1535
end if
1536
end if
15375
if sequence(file_name_p) then
15384
close(file_handle_)
1539
end if
15405
optimize(new_map_)
15415
return new_map_
1542
end function
1543
1544
--**
1545
-- Saves a map to a file.
1546
--
1547
-- Parameters:
1548
-- # ##m## : a map.
1549
-- # ##file_name_p## : Either a sequence, the name of the file to save to,
1550
-- or an open file handle as returned by [[:open]]().
1551
-- # ##type## : an integer. SM_TEXT for a human-readable format (default),
1552
-- SM_RAW for a smaller and faster format, but not human-readable.
1553
--
1554
-- Returns:
1555
-- An **integer**, the number of keys saved to the file, or -1 if the
1556
-- save failed.
1557
--
1558
-- Comments:
1559
-- If ##file_name_p## is an already opened file handle, this routine will write
1560
-- to that file and not close it. Otherwise, the named file will be created and
1561
-- closed by this routine.
1562
--
1563
-- The SM_TEXT type saves the map keys and values in a text format which can
1564
-- be read and edited by standard text editor. Each entry in the map is saved as
1565
-- a KEY/VALUE pair in the form \\
1566
-- {{{
1567
-- key = value
1568
-- }}}
1569
-- Note that if the 'key' value is a normal string value, it can be enclosed in
1570
-- double quotes. If it is not thus quoted, the first character of the key
1571
-- determines its Euphoria value type. A dash or digit implies an atom, an left-brace
1572
-- implies a sequence, an alphabetic character implies a text string that extends to the
1573
-- next equal '=' symbol, and anything else is ignored.
1574
--
1575
-- Note that if a line contains a double-dash, then all text from the double-dash
1576
-- to the end of the line will be ignored. This is so you can optionally add
1577
-- comments to the saved map. Also, any blank lines are ignored too.
1578
--
1579
-- All text after the '=' symbol is assumed to be the map item's value data.
1580
--
1581
-- The SM_RAW type saves the map in an efficient manner. It is generally smaller
1582
-- than the text format and is faster to process, but it is not human readable and
1583
-- standard text editors can not be used to edit it. In this format, the file will
1584
-- contain three serialized sequences:
1585
-- # Header sequence: {integer:format version, string: date and time of save (YYMMDDhhmmss),
1586
-- sequence: euphoria version {major, minor, revision, patch}}
1587
-- # Keys. A list of all the keys
1588
-- # Values. A list of the corresponding values for the keys.
1589
--
1590
-- Example 1:
1591
--
1592
-- map AppOptions
1593
-- if save_map(AppOptions, "c:\myapp\options.txt") = -1
1594
-- Error("Failed to save application options")
1595
-- end if
1596
-- if save_map(AppOptions, "c:\myapp\options.dat", SM_RAW) = -1
1597
-- Error("Failed to save application options")
1598
-- end if
1599
--
1600
--
1601
-- See Also:
1602
-- [[:load_map]]
1603
1604
public enum
160520
SM_TEXT,
160620
SM_RAW
1607
16084
16094
integer file_handle_ = -2
1610
sequence keys_
1611
sequence values_
1612
16134
if sequence(file_name_p) then
16143
if type_ = SM_TEXT then
16152
file_handle_ = open(file_name_p, "w")
1616
else
16171
file_handle_ = open(file_name_p, "wb")
1618
end if
1619
else
16201
file_handle_ = file_name_p
1621
end if
1622
16234
if file_handle_ < 0 then
16241
return -1
1625
end if
1626
16273
keys_ = keys(the_map_)
16283
values_ = values(the_map_)
1629
16303
if type_ = SM_RAW then
16312
puts(file_handle_, serialize(
1632
{1, -- saved map version
1633
datetime:format(now_gmt(), "%Y%m%d%H%M%S" ), -- date of this saved map
1634
{4,0,0,0}} -- Euphoria version
1635
))
16362
puts(file_handle_, serialize(keys_))
16372
puts(file_handle_, serialize(values_))
1638
else
16391
for i = 1 to length(keys_) do
164012
keys_[i] = pretty_sprint(keys_[i], {2,0,1,0,"%d","%.15g",32,127,1,0})
164112
keys_[i] = match_replace("-", keys_[i], "\\-")
164212
values_[i] = pretty_sprint(values_[i], {2,0,1,0,"%d","%.15g",32,127,1,0})
164312
values_[i] = match_replace("-", values_[i], "\\-")
1644
164512
printf(file_handle_, "%s = %s\n", {keys_[i], values_[i]})
1646
164712
end for
1648
end if
1649
16503
if sequence(file_name_p) then
16512
close(file_handle_)
1652
end if
16533
return length(keys_)
1654
end function
1655
1656
--**
1657
-- Duplicates a map.
1658
--
1659
-- Parameters:
1660
-- # ##source_map## : map to copy from
1661
-- # ##dest_map## : optional, map to copy to
1662
-- # ##put_operation## : optional, operation to use when ##dest##map## is used.
1663
-- The default is PUT.
1664
--
1665
-- Returns:
1666
-- If ##dest_map## was not provided, an exact duplicate of ##source_map## otherwise
1667
-- ##dest_map##, which does not have to be empty, is returned with the
1668
-- new values copied from ##source_map##, according to the ##put_operation## value.
1669
--
1670
-- Example 1:
1671
--
1672
-- map m1 = new()
1673
-- put(m1, 1, "one")
1674
-- put(m1, 2, "two")
1675
--
1676
-- map m2 = copy(m1)
1677
-- printf(1, "%s, %s\n", { get(m2, 1), get(m2, 2) })
1678
-- -- one, two
1679
--
1680
-- put(m1, 1, "one hundred")
1681
-- printf(1, "%s, %s\n", { get(m1, 1), get(m1, 2) })
1682
-- -- one hundred, two
1683
--
1684
-- printf(1, "%s, %s\n", { get(m2, 1), get(m2, 2) })
1685
-- -- one, two
1686
--
1687
--
1688
-- Example 2:
1689
--
1690
-- map m1 = new()
1691
-- map m2 = new()
1692
--
1693
-- put(m1, 1, "one")
1694
-- put(m1, 2, "two")
1695
-- put(m2, 3, "three")
1696
--
1697
-- copy(m1, m2)
1698
--
1699
-- ? keys(m2)
1700
-- -- { 1, 2, 3 }
1701
--
1702
--
1703
-- Example 3:
1704
--
1705
-- map m1 = new()
1706
-- map m2 = new()
1707
--
1708
-- put(m1, "XY", 1)
1709
-- put(m1, "AB", 2)
1710
-- put(m2, "XY", 3)
1711
--
1712
-- ? pairs(m1) -- { {"AB", 2}, {"XY", 1} }
1713
-- ? pairs(m2) -- { {"XY", 3} }
1714
--
1715
-- -- Add same keys' values.
1716
-- copy(m1, m2, ADD)
1717
--
1718
-- ? pairs(m2)
1719
-- -- { {"AB", 2}, {"XY", 4} }
1720
--
1721
--
1722
-- See Also:
1723
-- [[:put]]
1724
17255
1726
17275
if map(dest_map) then
1728
-- Copies the contents of one map to another map.
17294
sequence keys_set
17304
sequence value_set
17314
sequence source_data
1732
17334
source_data = ram_space[source_map]
17344
if source_data[MAP_TYPE] = LARGEMAP then
17353
for index = 1 to length(source_data[KEY_BUCKETS]) do
173657
keys_set = source_data[KEY_BUCKETS][index]
173757
value_set = source_data[VALUE_BUCKETS][index]
173857
for j = 1 to length(keys_set) do
17395
put(dest_map, keys_set[j], value_set[j], put_operation)
17405
end for
174157
end for
1742
else
17431
for index = 1 to length(source_data[FREE_LIST]) do
174450
if source_data[FREE_LIST][index] != 0 then
17452
put(dest_map, source_data[KEY_LIST][index], source_data[VALUE_LIST][index], put_operation)
1746
end if
174750
end for
1748
1749
end if
1750
17514
return dest_map
1752
else
17531
atom temp_map = malloc()
17541
ram_space[temp_map] = ram_space[source_map]
17551
return temp_map
1756
end if
1757
end function
1758
1759
1760
--**
1761
-- Converts a set of Key-Value pairs to a map.
1762
--
1763
-- Parameters:
1764
-- # ##kv_pairs## : A seqeuence containing any number of subsequences that
1765
-- have the format {KEY, VALUE}. These are loaded into a
1766
-- new map which is then returned by this function.
1767
--
1768
-- Returns:
1769
-- A **map**, containing the data from ##kv_pairs##
1770
--
1771
-- Example 1:
1772
--
1773
-- map m1 = new_from_kvpairs( {
1774
-- {"application", "Euphoria"},
1775
-- {"version", "4.0"},
1776
-- {"genre", "programming language"},
1777
-- {"crc", 0x4F71AE10}
1778
-- })
1779
--
1780
-- v = map:get(m1, "application") --> "Euphoria"
1781
--
1782
--
17833
1784
object new_map
1785
17863
new_map = new( floor(7 * length(kv_pairs) / 2) )
17873
for i = 1 to length(kv_pairs) do
178810
if length(kv_pairs[i]) = 2 then
178910
put(new_map, kv_pairs[i][1], kv_pairs[i][2])
1790
end if
179110
end for
1792
17933
return new_map
1794
1795
end function
1796
1797
--**
1798
-- Converts a set of Key-Value pairs contained in a string to a map.
1799
--
1800
-- Parameters:
1801
-- # ##kv_string## : A string containing any number of lines that
1802
-- have the format KEY=VALUE. These are loaded into a
1803
-- new map which is then returned by this function.
1804
--
1805
-- Returns:
1806
-- A **map**, containing the data from ##kv_string##
1807
--
1808
-- Comment:
1809
-- This function actually calls ##[[:keyvalues]]()## to convert the string to
1810
-- key-value pairs, which are then used to create the map.
1811
--
1812
-- Example 1:
1813
-- Given that a file called "xyz.config" contains the lines ...
1814
-- {{{
1815
-- application = Euphoria,
1816
-- version = 4.0,
1817
-- genre = "programming language",
1818
-- crc = 4F71AE10
1819
-- }}}
1820
--
1821
-- map m1 = new_from_string( read_file("xyz.config", TEXT_MODE))
1822
--
1823
-- printf(1, "%s\n", {map:get(m1, "application")}) --> "Euphoria"
1824
-- printf(1, "%s\n", {map:get(m1, "genre")}) --> "programming language"
1825
-- printf(1, "%s\n", {map:get(m1, "version")}) --> "4.0"
1826
-- printf(1, "%s\n", {map:get(m1, "crc")}) --> "4F71AE10"
1827
--
1828
--
1829
--
18302
18312
return new_from_kvpairs( keyvalues (kv_string) )
1832
end function
1833
1834
1835
--**
1836
-- Calls a user-defined routine for each of the items in a map.
1837
--
1838
-- Parameters:
1839
-- # ##source_map## : The map containing the data to process
1840
-- # ##user_rid##: The routine_id of a user defined processing function
1841
-- # ##user_data##: An object. Optional. This is passed, unchanged to each call
1842
-- of the user defined routine. By default, zero (0) is used.
1843
-- # ##in_sorted_order##: An integer. Optional. If non-zero the items in the
1844
-- map are processed in ascending key sequence otherwise
1845
-- the order is undefined. By default they are not sorted.
1846
--
1847
-- Returns:
1848
-- An integer: 0 means that all the items were processed, and anything else is whatever
1849
-- was returned by the user routine to abort the ##for_each()## process.
1850
--
1851
-- Comment:
1852
-- * The user defined routine is a function that must accept four parameters.
1853
-- ## Object: an Item Key
1854
-- ## Object: an Item Value
1855
-- ## Object: The ##user_data## value. This is never used by ##for_each()## itself,
1856
-- merely passed to the user routine.
1857
-- ## Integer: Progress code.
1858
-- *** The ##abs()## value of the progress code is the ordinal call number. That
1859
-- is 1 means the first call, 2 means the second call, etc ...
1860
-- *** If the progress code is negative, it is also the last call to the routine.
1861
-- *** If the progress code is zero, it means that the map is empty and thus the
1862
-- item key and value cannot be used.
1863
-- * The user routine must return 0 to get the next map item. Anything else will
1864
-- cause ##for_each()## to stop running, and is returned to whatever called
1865
-- ##for_each()##.
1866
--
1867
-- Example 1:
1868
--
1869
-- include std/map.e
1870
-- include std/math.e
1871
-- include std/io.e
1872
-- function Process_A(object k, object v, object d, integer pc)
1873
-- writefln("[] = []", {k, v})
1874
-- return 0
1875
-- end function
1876
--
1877
-- function Process_B(object k, object v, object d, integer pc)
1878
-- if pc = 0 then
1879
-- writefln("The map is empty")
1880
-- else
1881
-- integer c
1882
-- c = abs(pc)
1883
-- if c = 1 then
1884
-- writefln("---[]---", {d}) -- Write the report title.
1885
-- end if
1886
-- writefln("[]: [:15] = []", {c, k, v})
1887
-- if pc < 0 then
1888
-- writefln(repeat('-', length(d) + 6), {}) -- Write the report end.
1889
-- end if
1890
-- end if
1891
-- return 0
1892
-- end function
1893
--
1894
-- map m1 = new()
1895
-- map:put(m1, "application", "Euphoria")
1896
-- map:put(m1, "version", "4.0")
1897
-- map:put(m1, "genre", "programming language")
1898
-- map:put(m1, "crc", "4F71AE10")
1899
--
1900
-- -- Unsorted
1901
-- map:for_each(m1, routine_id("Process_A"))
1902
-- -- Sorted
1903
-- map:for_each(m1, routine_id("Process_B"), "List of Items", 1)
1904
--
1905
--
1906
-- The output from the first call could be...
1907
-- {{{
1908
-- application = Euphoria
1909
-- version = 4.0
1910
-- genre = programming language
1911
-- crc = 4F71AE10
1912
-- }}}
1913
--
1914
-- The output from the second call should be...
1915
-- {{{
1916
-- ---List of Items---
1917
-- 1: application = Euphoria
1918
-- 2: crc = 4F71AE10
1919
-- 3: genre = programming language
1920
-- 4: version = 4.0
1921
-- -------------------
1922
-- }}}
1923
--
19244
1925
sequence lKV
1926
object lRes
1927
19284
lKV = pairs(source_map, in_sorted_order)
19294
if length(lKV) = 0 then
19301
return call_func(user_rid, {0,0,user_data,0} )
1931
end if
1932
19333
for i = 1 to length(lKV) do
193410
if i = length(lKV) then
19352
lRes = call_func(user_rid, {lKV[i][1], lKV[i][2], user_data, -i})
1936
else
19378
lRes = call_func(user_rid, {lKV[i][1], lKV[i][2], user_data, i})
1938
end if
193910
if not equal(lRes, 0) then
19401
return lRes
1941
end if
19429
end for
19432
return 0
1944
end function
1945
1946
-- LOCAL FUNCTIONS --
19471
1948
sequence temp_map_
1949
atom map_handle_
1950
19511
temp_map_ = ram_space[the_map_]
1952
19531
map_handle_ = new()
19541
for index = 1 to length(temp_map_[FREE_LIST]) do
195511
if temp_map_[FREE_LIST][index] != 0 then
195611
put(map_handle_, temp_map_[KEY_LIST][index], temp_map_[VALUE_LIST][index])
1957
end if
195811
end for
1959
19601
ram_space[the_map_] = ram_space[map_handle_]
19611
end procedure
1962
19633
1964
sequence keys_
1965
sequence values_
1966
19673
keys_ = keys(the_map_)
19683
values_ = values(the_map_)
1969
19703
ram_space[the_map_] = {type_is_map, 0,0, SMALLMAP, repeat(init_small_map_key, threshold_size), repeat(0, threshold_size), repeat(0, threshold_size)}
1971
19723
for i = 1 to length(keys_) do
19738
put(the_map_, keys_[i], values_[i], PUT, 0)
19748
end for
1975
19763
end procedure
1977