planckforth/lib/table.fs

231 lines
5.4 KiB
Forth
Raw Permalink Normal View History

2021-12-03 04:32:34 +01:00
\ planckforth -
\ Copyright (C) 2021 nineties
( === Hash Table === )
include lib/bitscan.fs
include lib/array.fs
private{
2021-12-03 22:27:59 +01:00
s" Key not found" exception constant KEY-NOT-FOUND export
2021-12-03 04:32:34 +01:00
create prime_numbers
5 , 11 , 17 , 37 , 67 , 131 , 257 , 521 , 1031 ,
2053 , 4099 , 8209 , 16411 , 32771 , 65537 , 131101 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 ,
struct
2021-12-03 13:30:56 +01:00
cell% field table>bucket
cell% field table>hash ( hash function )
cell% field table>equal ( equal function for keys )
cell% field table>entries ( list of entries )
2021-12-03 04:32:34 +01:00
int% field table>size ( number of entries )
end-struct table%
struct
2021-12-03 13:30:56 +01:00
cell% field entry>key
cell% field entry>value
cell% field entry>sibling ( pointer to the next entry in bucket )
cell% field entry>next ( pointer to the next entry in entries )
2021-12-03 04:32:34 +01:00
int% field entry>hash ( the hash value )
end-struct entry%
2021-12-04 12:15:28 +01:00
\ Number of elments in the table
2021-12-03 22:27:59 +01:00
: table-size ( tbl -- n ) table>size @ ; export
2021-12-04 12:15:28 +01:00
\ Make hashtable considering given size hint
2021-12-03 13:30:56 +01:00
: make-table-with-hint ( hash equal n -- tbl )
2021-12-03 04:32:34 +01:00
bitscan-reverse cells prime_numbers + @ ( n to bucket size )
make-array ( allocate bucket )
( hash equal bucket )
table% %allocate throw
tuck table>bucket !
tuck table>equal !
tuck table>hash !
0 over table>entries !
0 over table>size !
2021-12-03 14:00:30 +01:00
;
2021-12-03 04:32:34 +01:00
2021-12-03 13:30:56 +01:00
10 constant DEFAULT_TABLE_SIZE_HINT
2021-12-04 12:15:28 +01:00
\ Make hashtable. It takes two functions.
\ hash ( w -- n ) : compute hash value of w
\ equal ( w1 w2 -- n ) : compute equality of w1 and w2
2021-12-03 13:30:56 +01:00
: make-table ( hash equal -- tbl )
DEFAULT_TABLE_SIZE_HINT make-table-with-hint
; export
2021-12-03 14:00:30 +01:00
: release-table ( tbl -- )
dup table>entries @
begin ?dup while
dup entry>next @
swap
free
repeat
dup table>bucket @ release-array
free
; export
2021-12-04 11:22:25 +01:00
: find-entry ( key tbl hash -- entry )
2021-12-03 22:37:59 +01:00
over table>bucket @ array-size mod ( key tbl idx )
over table>bucket @ array@ ( key tbl entry )
swap table>equal @ -rot ( equal key entry )
2021-12-03 22:27:59 +01:00
begin ?dup while
dup entry>key @
2 pick 4 pick execute if
2021-12-03 22:37:59 +01:00
( equal key entry )
nip nip exit
2021-12-03 22:27:59 +01:00
then
2021-12-04 11:22:25 +01:00
entry>next @
2021-12-03 22:27:59 +01:00
repeat
2021-12-03 22:37:59 +01:00
2drop 0
;
2021-12-04 12:15:28 +01:00
\ Lookup table entry. KEY-NOT-FOUND exception is raised
\ when there is no corresponding entry.
2021-12-04 11:22:25 +01:00
: table@ ( key tbl -- val )
2dup
2dup table>hash @ execute
find-entry ?dup if
2021-12-03 22:37:59 +01:00
entry>value @ nip nip
else
KEY-NOT-FOUND throw
then
; export
2021-12-04 12:15:28 +01:00
\ Returns true when the key is in the table
2021-12-03 22:37:59 +01:00
: ?table-in ( key tbl -- n )
2021-12-04 11:22:25 +01:00
2dup table>hash @ execute
find-entry 0 <>
; export
2021-12-04 12:15:28 +01:00
\ Store key-value pair to the table
2021-12-04 11:22:25 +01:00
: table! ( val key tbl -- )
2dup
2dup table>hash @ execute
dup >r find-entry r> swap
( val key tbl hash entry )
?dup if
( `tbl` already has an entry for `key` )
nip nip nip
entry>value !
else
swap >r
( val key hash , R:tbl )
entry% %allocate throw
tuck entry>hash !
tuck entry>key !
tuck entry>value !
0 over entry>sibling !
0 over entry>next !
r>
( entry tbl )
\ Find corresponding bucket entry
over entry>hash @
over table>bucket @
tuck array-size mod
( entry tbl bucket index )
\ Add new entry to the bucket
2dup swap array@
4 pick entry>sibling !
3 pick swap rot array!
\ Add the entry to the list of entries
( entry tbl )
tuck table>entries @
over entry>next !
over table>entries !
\ Increment table>size
table>size 1 swap +!
then
2021-12-03 22:27:59 +01:00
; export
2021-12-04 12:15:28 +01:00
\ Returns cons-list of keys
: table-keys ( tbl -- list )
0 swap table>entries @
begin ?dup while
tuck entry>key @ swap cons swap
entry>next @
repeat
; export
\ Returns cons-list of values
: table-values ( tbl -- list )
0 swap table>entries @
begin ?dup while
tuck entry>value @ swap cons swap
entry>next @
repeat
; export
( === tables for major builtin types === )
2021-12-03 14:00:30 +01:00
: hash-next ( n1 n2 -- n3 )
+ 6122117 * 1627577 +
;
: hash-int ( n -- n )
0 hash-next
2021-12-04 12:26:36 +01:00
;
2021-12-03 14:00:30 +01:00
: make-int-table ( -- tbl )
['] hash-int ['] = make-table
; export
2021-12-04 12:26:36 +01:00
: hash-string ( s -- n )
0 begin over c@ dup while
hash-next
swap 1+ swap
repeat drop nip
;
: make-string-table ( -- tbl )
['] hash-string ['] streq make-table
; export
2021-12-03 04:32:34 +01:00
}private
2021-12-03 14:00:30 +01:00
2021-12-03 22:27:59 +01:00
T{ make-int-table constant A -> }T
T{ A table-size -> 0 }T
T{ 0 A ' table@ catch -> 0 A KEY-NOT-FOUND }T
2021-12-03 22:37:59 +01:00
T{ 0 A ?table-in -> false }T
2021-12-04 11:22:25 +01:00
T{ 1 0 A table! -> }T
T{ 0 A ?table-in -> true }T
T{ 0 A table@ -> 1 }T
T{ A table-size -> 1 }T
T{ 2 0 A table! -> }T
T{ A table-size -> 1 }T
T{ 3 1 A table! -> }T
T{ A table-size -> 2 }T
T{ 1 A table@ -> 3 }T
T{ :noname 100 0 do i 1 + i A table! loop ; execute -> }T
2021-12-31 11:56:53 +01:00
T{ 0 A table@ -> 1 }T
T{ 99 A table@ -> 100 }T
2021-12-04 11:22:25 +01:00
T{ A table-size -> 100 }T
2021-12-04 12:15:28 +01:00
T{ A table-keys car -> 0 }T
T{ A table-keys cdr car -> 1 }T
T{ A table-keys cdr cdr car -> 2 }T
T{ A table-values car -> 1 }T
T{ A table-values cdr car -> 2 }T
T{ A table-values cdr cdr car -> 3 }T
2021-12-04 12:25:04 +01:00
T{ A release-table -> }T
2021-12-04 12:26:36 +01:00
T{ make-string-table constant A -> }T
2021-12-04 12:53:33 +01:00
T{ s" zero" make-string constant ZERO -> }T
T{ s" one" make-string constant ONE -> }T
T{ 0 ZERO A table! -> }T
T{ 1 ONE A table! -> }T
T{ ZERO A table@ -> 0 }T
T{ ONE A table@ -> 1 }T
2021-12-04 12:26:36 +01:00
T{ s" zero" A table@ -> 0 }T
T{ s" one" A table@ -> 1 }T
T{ A release-table -> }T
2021-12-04 12:53:33 +01:00
T{ ZERO release-string -> }T
T{ ONE release-string -> }T