-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
238 lines (197 loc) · 4.97 KB
/
main.go
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package estraier
import (
"strings"
"syscall"
"unsafe"
)
var estraier = syscall.NewLazyDLL("estraier.dll")
var estOpen = estraier.NewProc("est_db_open")
var estClose = estraier.NewProc("est_db_close")
var estCondNew = estraier.NewProc("est_cond_new")
var estCondSetPhrase = estraier.NewProc("est_cond_set_phrase")
var estCondSetOptions = estraier.NewProc("est_cond_set_options")
var estCondAddAttr = estraier.NewProc("est_cond_add_attr")
var estCondDelete = estraier.NewProc("est_cond_delete")
var estDbSearch = estraier.NewProc("est_db_search")
var estDbGetDoc = estraier.NewProc("est_db_get_doc")
var estDocDelete = estraier.NewProc("est_doc_delete")
var estDocAttr = estraier.NewProc("est_doc_attr")
var estErrMsg = estraier.NewProc("est_err_msg")
var msvcrt = syscall.NewLazyDLL("msvcrt.dll")
var free = msvcrt.NewProc("free")
var memcpy = msvcrt.NewProc("memcpy")
type Database uintptr
const (
forRead = 1
)
type EstError uint32
const (
// ESTENOERR means no error
ESTENOERR EstError = iota
// ESTEINVAL means invalid argument
ESTEINVAL
// ESTEACCES means access forbidden
ESTEACCES
// ESTELOCK means lock failure
ESTELOCK
// ESTEDB means database problem
ESTEDB
// ESTEIO means I/O problem
ESTEIO
// ESTENOITEM means no item
ESTENOITEM
// ESTEMISC means miscellaneous
ESTEMISC EstError = 9999
)
func (ecode EstError) Error() string {
msg, _, _ := estErrMsg.Call(uintptr(ecode))
return cstr2string(msg)
}
func (ecode *EstError) address() uintptr {
return uintptr(unsafe.Pointer(ecode))
}
func cstr2string(cstr uintptr) string {
if cstr == 0 {
return ""
}
var buffer strings.Builder
for {
c := *(*byte)(unsafe.Pointer(cstr))
if c == 0 {
break
}
buffer.WriteByte(c)
cstr++
}
return buffer.String()
}
func lastError(ecode EstError) error {
if ecode == ESTENOERR {
return nil
}
return ecode
}
func (db Database) Close() error {
ecode := ESTEMISC
estClose.Call(uintptr(db), ecode.address())
return lastError(ecode)
}
func address(s string) uintptr {
bin := []byte(s)
return uintptr(unsafe.Pointer(&bin[0]))
}
func Open(dbPath string) (Database, error) {
ecode := ESTEMISC
db, _, _ := estOpen.Call(
address(dbPath),
forRead,
ecode.address())
return Database(db), lastError(ecode)
}
type ConditionsContainer uintptr
func newCond() ConditionsContainer {
cond, _, _ := estCondNew.Call()
return ConditionsContainer(cond)
}
func (cond ConditionsContainer) setPhrase(expr string) {
if len(expr) > 0 {
estCondSetPhrase.Call(uintptr(cond), address(expr))
}
}
func (cond ConditionsContainer) setOptions(options uintptr) {
estCondSetOptions.Call(uintptr(cond), options)
}
func (cond ConditionsContainer) addAttr(options string) {
estCondAddAttr.Call(uintptr(cond), address(options))
}
func (cond ConditionsContainer) close() {
estCondDelete.Call(uintptr(cond))
}
type DocID int
func (db Database) search(cond ConditionsContainer) []DocID {
var num int32
pages, _, _ := estDbSearch.Call(
uintptr(db),
uintptr(cond),
uintptr(unsafe.Pointer(&num)),
0)
if num <= 0 {
return []DocID{}
}
result := make([]DocID, num)
memcpy.Call(uintptr(unsafe.Pointer(&result[0])), pages, uintptr(4*num))
free.Call(pages)
return result
}
type Phrase string
func (phrase Phrase) Join(cond ConditionsContainer) {
cond.setPhrase(string(phrase))
}
type Option uintptr
const (
// Sure means check every N-gram key
Sure Option = 1 << 0
// Usual means check N-gram keys skipping by one
Usual Option = 1 << 1
// Fast means check N-gram keys skipping by two
Fast Option = 1 << 2
// Agito means check N-gram keys skipping by three
Agito Option = 1 << 3
// Noidf means without TF-IDF tuning
Noidf Option = 1 << 4
// Simple menas with the simplified phrase
Simple Option = 1 << 10
// Rough menas with the rough phrase
Rough Option = 1 << 11
// Union means with the union phrase
Union Option = 1 << 15
// Isect means with the intersection phrase
Isect Option = 1 << 16
// Scfb means feed back scores (for debug)
Scfb Option = 1 << 30
)
func (option Option) Join(cond ConditionsContainer) {
cond.setOptions(uintptr(option))
}
type CondAttr string
func (condAttr CondAttr) Join(cond ConditionsContainer) {
cond.addAttr(string(condAttr))
}
// Condition is the interface for conditions.
// (Database)Search can receive objects satisfying Condition.
type Condition interface {
Join(ConditionsContainer)
}
// Search searches documents satisfying conditions.
func (db Database) Search(conditions ...Condition) []DocID {
cond := newCond()
for _, c1 := range conditions {
c1.Join(cond)
}
rc := db.search(cond)
cond.close()
return rc
}
type Doc uintptr
func (db Database) GetDoc(id DocID) Doc {
doc, _, _ := estDbGetDoc.Call(
uintptr(db),
uintptr(id),
0)
return Doc(doc)
}
func (doc Doc) Close() {
if doc != 0 {
estDocDelete.Call(uintptr(doc))
}
}
func (doc Doc) Attr(attr string) string {
if doc == 0 {
return ""
}
value, _, _ := estDocAttr.Call(uintptr(doc), address(attr))
return cstr2string(value)
}
func (doc Doc) URI() string {
return doc.Attr("@uri")
}