Advertisement
Skytrias

Bitfield Trie and CTrie Update 1

Oct 28th, 2022 (edited)
1,934
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 8.51 KB | Software | 0 0
  1. package btrie
  2.  
  3. import "core:os"
  4. import "core:mem"
  5. import "core:math/bits"
  6. import "core:fmt"
  7. import "core:strings"
  8.  
  9. ALPHABET_SIZE :: 26
  10. SHORTCUT_VALUE :: 0xFC000000 //w upper 6 bits set from u32
  11. ALPHABET_MASK :: u32(1 << 26) - 1
  12.  
  13. /*
  14.     CTrie (104B)
  15.         uses smallest index possible to reduce size constraints
  16.         data allocated in array -> easy to free/clear/delete
  17.         should be the same speed of the default Trie
  18. */
  19.  
  20. // treating 0 as nil as 0 is the root
  21. CTrie :: struct #packed {
  22.     array: [ALPHABET_SIZE]u32,
  23.     count: u8, // count of used array fields
  24. }
  25.  
  26. ctrie_data: [dynamic]CTrie
  27.  
  28. ctrie_init :: proc(cap: int) {
  29.     ctrie_data = make([dynamic]CTrie, 0, cap)
  30.     append(&ctrie_data, CTrie {})
  31. }
  32.  
  33. ctrie_destroy :: proc() {
  34.     delete(ctrie_data)
  35. }
  36.  
  37. // push a node to the ctrie array and return its index
  38. ctrie_push :: proc() -> u32 {
  39.     append(&ctrie_data, CTrie {})
  40.     return u32(len(ctrie_data) - 1)
  41. }
  42.  
  43. // simple helper
  44. ctrie_get :: #force_inline proc(index: u32) -> ^CTrie {
  45.     return &ctrie_data[index]
  46. }
  47.  
  48. // get the root of the ctrie tree
  49. ctrie_root :: #force_inline proc() -> ^CTrie {
  50.     return &ctrie_data[0]
  51. }
  52.  
  53. // insert a key
  54. ctrie_insert :: proc(key: string) {
  55.     t := ctrie_root()
  56.  
  57.     for b in key {
  58.         idx := b - 'a'
  59.  
  60.         if t.array[idx] == 0 {
  61.             t.array[idx] = ctrie_push()
  62.             t.count += 1
  63.         }
  64.  
  65.         t = ctrie_get(t.array[idx])
  66.     }  
  67. }
  68.  
  69. // print the ctrie tree
  70. ctrie_print :: proc() {
  71.     depth: int
  72.     ctrie_print_recursive(ctrie_root(), &depth, 0)
  73. }
  74.  
  75. // print the ctrie tree recursively by nodes
  76. ctrie_print_recursive :: proc(t: ^CTrie, depth: ^int, b: u8) {
  77.     if depth^ != 0 {
  78.         for i in 0..<depth^ - 1 {
  79.             fmt.eprint('\t')
  80.         }
  81.         codepoint := rune(b + 'a')
  82.         fmt.eprint(codepoint, '\n')
  83.     }
  84.  
  85.     for i in 0..<ALPHABET_SIZE {
  86.         if t.array[i] != 0 {
  87.             depth^ += 1
  88.             ctrie_print_recursive(ctrie_get(t.array[i]), depth, u8(i))
  89.             depth^ -= 1
  90.         }
  91.     }
  92. }
  93.  
  94. // DEBUG print the ctrie tree size
  95. ctrie_print_size :: proc() {
  96.     size := len(ctrie_data) * size_of(CTrie)
  97.     fmt.eprintf("SIZE in %dB %dKB %dMB for CTrie\n", size, size / 1024, size / 1024 / 1024)
  98. }
  99.  
  100. /*
  101.     Compressed trie in flat memory
  102.         uses the least amount of memory possible while still being accessible
  103.         bitfield with 32 bits describing 26 wanted bits (a-z)
  104. */
  105.  
  106. // dynamic byte array containing flexible compressed trie data
  107. comp: []byte
  108. comp_index: int
  109.  
  110. // init the comp to some cap
  111. comp_init :: proc(cap := mem.Megabyte) {
  112.     comp = make([]byte, cap)
  113. }
  114.  
  115. // destroy the comp data
  116. comp_destroy :: proc() {
  117.     delete(comp)   
  118. }
  119.  
  120. // push u32 alphabet bits data
  121. comp_push_bits :: proc() -> (res: ^u32) {
  122.     old := comp_index
  123.     res = cast(^u32) &comp[old]
  124.     comp_index += size_of(u32)
  125.     return
  126. }
  127.  
  128. // push trie children nodes as indexes
  129. comp_push_data :: proc(count: int) -> (res: []u32) {
  130.     old := comp_index
  131.     res = mem.slice_ptr(cast(^u32) &comp[old], count)
  132.     comp_index += size_of(u32) * count
  133.     return
  134. }
  135.  
  136. // baking data when single lane only
  137. single_characters: [256]u8
  138. single_index: int
  139.  
  140. ctrie_check_single_only :: proc(t: ^CTrie) -> bool {
  141.     if t.count ==0 || t.count == 1 {
  142.         for i in 0..<ALPHABET_SIZE {
  143.             if t.array[i] != 0 {
  144.                 // prebake data already
  145.                 single_characters[single_index] = u8(i) + 'a'
  146.                 single_index += 1
  147.  
  148.                 return ctrie_check_single_only(ctrie_get(t.array[i]))
  149.             }
  150.         }
  151.  
  152.         return true
  153.     } else {
  154.         return false
  155.     }
  156. }
  157.  
  158. comp_push_shortcut :: proc(t: ^CTrie) {
  159.     for i in 0..<single_index {
  160.         comp[comp_index] = single_characters[i]
  161.         comp_index += 1
  162.     }  
  163. }
  164.  
  165. // push a ctrie bitfield and its dynamic data
  166. comp_push_ctrie :: proc(t: ^CTrie, previous_data: ^u32) {
  167.     if previous_data != nil {
  168.         previous_data^ = u32(comp_index)
  169.     }
  170.  
  171.     alphabet_bits := comp_push_bits()
  172.  
  173.     // on valid sub nodes -> insert data
  174.     if t.count != 0 {
  175.         field: u32
  176.  
  177.         // check for single branches only
  178.         if t.count == 1 {
  179.             single_index = 0
  180.             single_only := ctrie_check_single_only(t)
  181.             // fmt.eprintln("single_only?", single_only)
  182.  
  183.             if single_only {
  184.                 // insert shortcut signal
  185.                 field = SHORTCUT_VALUE
  186.                 // insert string length
  187.                 field = bits.bitfield_insert_u32(field, u32(single_index), 0, 26)
  188.                 // insert characters
  189.                 comp_push_shortcut(t)
  190.             }
  191.         }
  192.  
  193.         // if nothing was set
  194.         if field == 0 {
  195.             data := comp_push_data(int(t.count))
  196.             index: int
  197.  
  198.             for i in 0..<uint(ALPHABET_SIZE) {
  199.                 if t.array[i] != 0 {
  200.                     field = bits.bitfield_insert_u32(field, 1, i, 1)
  201.                     comp_push_ctrie(ctrie_get(t.array[i]), &data[index])
  202.                     index += 1
  203.                 }
  204.             }
  205.         }
  206.  
  207.         // only set once
  208.         alphabet_bits^ = field
  209.     }
  210. }
  211.  
  212. // DEBUG print the trie tree
  213. comp_print :: proc() {
  214.     assert(comp_index != 0)
  215.     depth: int
  216.     comp_print_recursive(cast(^u32) &comp[0], &depth)
  217. }
  218.  
  219. // DEBUG print the trie tree recursively
  220. comp_print_recursive :: proc(alphabet_bits: ^u32, depth: ^int) {
  221.     b := alphabet_bits^
  222.  
  223.     if b == 0 {
  224.         return
  225.     }
  226.  
  227.     // check for shortcut bits
  228.     if comp_bits_is_shortcut(b) {
  229.         depth^ += 1
  230.         for i in 0..<depth^ - 1 {
  231.             fmt.eprint('\t')
  232.         }
  233.  
  234.         text := comp_bits_shortcut_text(alphabet_bits)
  235.         fmt.eprintln(text)
  236.  
  237.         depth^ -= 1
  238.         return
  239.     }
  240.  
  241.     bit_index: int
  242.     for i := 0; b > 0; i += 1 {
  243.         if b & 1 == 1 {
  244.             // search for matching bits
  245.             depth^ += 1
  246.             for j in 0..<depth^ - 1 {
  247.                 fmt.eprint('\t')
  248.             }
  249.             codepoint := rune(i + 'a')
  250.             fmt.eprint(codepoint, '\n')
  251.  
  252.             next := mem.ptr_offset(alphabet_bits, bit_index + 1)
  253.             comp_print_recursive(cast(^u32) &comp[next^], depth)
  254.             depth^ -= 1
  255.             bit_index += 1
  256.         }
  257.  
  258.         b >>= 1
  259.     }
  260. }
  261.  
  262. // print logical size of the compressed trie
  263. comp_print_size :: proc() {
  264.     size := comp_index
  265.     fmt.eprintf("SIZE in %dB %dKB %dMB for compressed\n", size, size / 1024, size / 1024 / 1024)
  266. }
  267.  
  268. // converts alphabetic byte index into the remapped bitfield space
  269. comp_bits_index_to_counted_one :: proc(field: u32, idx: u32) -> (res: u32, ok: bool) {
  270.     b := field
  271.  
  272.     for i := u32(0); b > 0; i += 1 {
  273.         if b & 1 == 1 {
  274.             if idx == i {
  275.                 ok = true
  276.                 return
  277.             }
  278.  
  279.             res += 1
  280.         }
  281.  
  282.         b >>= 1
  283.     }
  284.  
  285.     return
  286. }
  287.  
  288. // DEBUG prints used characters in the bitset
  289. comp_print_characters :: proc(field: u32) {
  290.     if field == 0 {
  291.         fmt.eprintln("EMPTY BITS")
  292.         return
  293.     }
  294.  
  295.     b := field
  296.     for i := u32(0); b > 0; i += 1 {
  297.         if b & 1 == 1 {
  298.             fmt.eprint(rune(i + 'a'), ' ')
  299.         }
  300.  
  301.         b >>= 1
  302.     }
  303.  
  304.     fmt.eprintln()
  305. }
  306.  
  307. // TODO escape on utf8 byte?
  308. // lowercase valid alpha
  309. ascii_check_lower :: proc(b: u8) -> u8 {
  310.     if 'A' <= b && b <= 'Z' {
  311.         return b + 32
  312.     } else {
  313.         return b
  314.     }
  315. }
  316.  
  317. // wether the byte is a letter
  318. ascii_is_letter :: #force_inline proc(b: u8) -> bool {
  319.     return 'a' <= b && b <= 'z'
  320. }
  321.  
  322. // less pointer offseting sent by Jeroen :)
  323. comp_search :: proc(key: string) -> bool {
  324.     alphabet_bits := cast(^u32) &comp[0]
  325.  
  326.     for i := 0; i < len(key); i += 1 {
  327.         b := ascii_check_lower(key[i])
  328.  
  329.         if ascii_is_letter(b) {
  330.             letter := b - 'a'
  331.  
  332.             // check for shortcut first!
  333.             if comp_bits_is_shortcut(alphabet_bits^) {
  334.                 // fmt.eprintln("check bits", key)
  335.                 rest := comp_bits_shortcut_text(alphabet_bits)
  336.                 assert(len(rest) != 0)
  337.                 rest_index: int
  338.  
  339.                 // match the rest letters
  340.                 for j in i..<len(key) {
  341.                     b = ascii_check_lower(key[j])
  342.                    
  343.                     if ascii_is_letter(b) {
  344.                         // range check
  345.                         if rest_index < len(rest) {
  346.                             if b != rest[rest_index] {
  347.                                 return false
  348.                             }
  349.                         }
  350.                     } else {
  351.                         return false
  352.                     }
  353.  
  354.                     rest_index += 1
  355.                 }
  356.             } else {
  357.                 // extract bit info
  358.                 if res, ok := comp_bits_index_to_counted_one(alphabet_bits^, u32(letter)); ok {
  359.                     next := mem.ptr_offset(alphabet_bits, res + 1)
  360.                     alphabet_bits = cast(^u32) &comp[next^]
  361.                 } else {
  362.                     return false
  363.                 }
  364.             }
  365.         } else {
  366.             return false
  367.         }
  368.     }
  369.    
  370.     return true
  371. }
  372.  
  373. // check if the field includes a shortcut
  374. comp_bits_is_shortcut :: #force_inline proc(field: u32) -> bool {
  375.     return (field & SHORTCUT_VALUE) == SHORTCUT_VALUE
  376. }
  377.  
  378. // extract shortcut length
  379. comp_bits_shortcut_length :: #force_inline proc(field: u32) -> u32 {
  380.     return bits.bitfield_extract_u32(field, 0, 26)
  381. }
  382.  
  383. // get the shortcut text as a string from the ptr
  384. comp_bits_shortcut_text :: proc(field: ^u32) -> string {
  385.     length := comp_bits_shortcut_length(field^)
  386.    
  387.     return strings.string_from_ptr(
  388.         cast(^u8) mem.ptr_offset(field, 1),
  389.         int(length),
  390.     )
  391. }
  392.  
  393. comp_write_to_file :: proc(path: string) -> bool {
  394.     return os.write_entire_file(path, comp[:comp_index])
  395. }
  396.  
  397. comp_read_from_file :: proc(path: string) {
  398.     content, ok := os.read_entire_file(path)
  399.  
  400.     if ok {
  401.         comp = content
  402.         comp_index = len(content)
  403.     }
  404. }
Tags: Trie
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement