Include ending tag in tile sequence.
[lightning.git] / taglib.go
1 package main
2
3 import (
4         "bufio"
5         "bytes"
6         "fmt"
7         "io"
8 )
9
10 const tagmapKeySize = 24
11
12 type tagmapKey [tagmapKeySize]byte
13
14 type tagID int32
15
16 type tagInfo struct {
17         id     tagID // 0-based position in input tagset
18         tagseq []byte
19 }
20
21 type tagLibrary struct {
22         tagmap map[tagmapKey]tagInfo
23         keylen int
24 }
25
26 func (taglib *tagLibrary) Load(rdr io.Reader) error {
27         var seqs [][]byte
28         scanner := bufio.NewScanner(rdr)
29         for scanner.Scan() {
30                 data := scanner.Bytes()
31                 if len(data) > 0 && data[0] == '>' {
32                 } else {
33                         seqs = append(seqs, append([]byte(nil), data...))
34                 }
35         }
36         if err := scanner.Err(); err != nil {
37                 return err
38         }
39         return taglib.setTags(seqs)
40 }
41
42 func (taglib *tagLibrary) FindAll(buf []byte, fn func(id tagID, pos, taglen int)) {
43         var key tagmapKey
44         for i := 0; i <= len(buf)-taglib.keylen; i++ {
45                 copy(key[:taglib.keylen], buf[i:])
46                 if taginfo, ok := taglib.tagmap[key]; !ok {
47                         continue
48                 } else if len(taginfo.tagseq) > taglib.keylen && (len(buf) < i+len(taginfo.tagseq) || !bytes.Equal(taginfo.tagseq, buf[i:i+len(taginfo.tagseq)])) {
49                         // key portion matches, but not the entire tag
50                         continue
51                 } else {
52                         fn(taginfo.id, i, len(taginfo.tagseq))
53                         i += len(taginfo.tagseq) - 1 // don't try to match overlapping tags
54                 }
55         }
56 }
57
58 func (taglib *tagLibrary) Len() int {
59         return len(taglib.tagmap)
60 }
61
62 func (taglib *tagLibrary) setTags(tags [][]byte) error {
63         taglib.keylen = tagmapKeySize
64         for _, t := range tags {
65                 if l := len(t); taglib.keylen > l {
66                         taglib.keylen = l
67                 }
68         }
69         taglib.tagmap = map[tagmapKey]tagInfo{}
70         for i, t := range tags {
71                 t = bytes.ToLower(t)
72                 var key tagmapKey
73                 copy(key[:], t[:taglib.keylen])
74                 if _, ok := taglib.tagmap[key]; ok {
75                         return fmt.Errorf("first %d bytes of tag %d (%s) are not unique", taglib.keylen, i, key)
76                 }
77                 taglib.tagmap[key] = tagInfo{tagID(i), t}
78         }
79         return nil
80 }