about summary refs log tree commit diff stats
path: root/src/html/catom.nim
blob: e908a5f43e8bc8be71c3231a133b75d904baa3cb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import std/hashes

import html/enums

import chame/tags

#TODO use a better hash map
const CAtomFactoryStrMapLength = 1024 # must be a power of 2
static:
  doAssert (CAtomFactoryStrMapLength and (CAtomFactoryStrMapLength - 1)) == 0

# Null atom + mapped tag types + mapped attr types
const AttrMapNum = 1 + ({TagType.low..TagType.high} - {TAG_UNKNOWN}).card +
  ({AttrType.low..AttrType.high} - {atUnknown}).card

type
  CAtom* = distinct int

  CAtomFactoryInit = object
    obj: CAtomFactoryObj
    attrToAtom: array[AttrType, CAtom]
    atomToAttr: array[AttrMapNum, AttrType]

  CAtomFactoryObj = object
    strMap: array[CAtomFactoryStrMapLength, seq[CAtom]]
    atomMap: seq[string]

  #TODO could be a ptr probably
  CAtomFactory* = ref CAtomFactoryObj

const CAtomNull* = CAtom(0)

# Mandatory Atom functions
func `==`*(a, b: CAtom): bool {.borrow.}
func hash*(atom: CAtom): Hash {.borrow.}

func `$`*(a: CAtom): string {.borrow.}

func toAtom(factory: var CAtomFactoryObj, s: string): CAtom =
  let h = s.hash()
  let i = h and (factory.strMap.len - 1)
  for atom in factory.strMap[i]:
    if factory.atomMap[int(atom)] == s:
      # Found
      return atom
  # Not found
  let atom = CAtom(factory.atomMap.len)
  factory.atomMap.add(s)
  factory.strMap[i].add(atom)
  return atom

const factoryInit = (func(): CAtomFactoryInit =
  var init = CAtomFactoryInit()
  # Null atom
  init.obj.atomMap.add("")
  # TagType: 1..TagType.high
  for tagType in TagType(1) .. TagType.high:
    discard init.obj.toAtom($tagType)
  # Attr: may overlap with TagType; exclude atUnknown
  for attrType in AttrType(1) .. AttrType.high:
    let atom = init.obj.toAtom($attrType)
    init.attrToAtom[attrType] = atom
    init.atomToAttr[int(atom)] = attrType
  return init
)()

proc newCAtomFactory*(): CAtomFactory =
  let factory = new(CAtomFactory)
  factory[] = factoryInit.obj
  return factory

func toAtom*(factory: CAtomFactory, s: string): CAtom =
  return factory[].toAtom(s)

func toAtom*(factory: CAtomFactory, tagType: TagType): CAtom =
  assert tagType != TAG_UNKNOWN
  return CAtom(tagType)

func toAtom*(factory: CAtomFactory, attrType: AttrType): CAtom =
  assert attrType != atUnknown
  return factoryInit.attrToAtom[attrType]

func toStr*(factory: CAtomFactory, atom: CAtom): string =
  return factory.atomMap[int(atom)]

func toTagType*(factory: CAtomFactory, atom: CAtom): TagType =
  let i = int(atom)
  if i in 1 .. int(TagType.high):
    return TagType(atom)
  return TAG_UNKNOWN

func toAttrType*(factory: CAtomFactory, atom: CAtom): AttrType =
  let i = int(atom)
  if i < factoryInit.atomToAttr.len:
    return factoryInit.atomToAttr[i]
  return atUnknown