Skip to content

Commit 477dde7

Browse files
committed
init
0 parents  commit 477dde7

20 files changed

Lines changed: 31446 additions & 0 deletions

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
Device Detection based on 51Degrees Trie. Golang version of [51Degrees/Device-Detection](https://github.com/51Degrees/Device-Detection) without cgo.
2+
3+
## Installation
4+
5+
`go get github.com/IncSW/go-51degrees`
6+
7+
```go
8+
import fiftyonedegrees "github.com/IncSW/go-51degrees"
9+
```
10+
11+
## Quick Start
12+
13+
```go
14+
reader, err := fiftyonedegrees.NewReaderFromFile("path/to/51DegreesV3.4.trie")
15+
if err != nil {
16+
panic(err)
17+
}
18+
device := reader.MatchDevice("UserAgent")
19+
fmt.Println(device.GetValue("BrowserName"), device.GetValue("BrowserVersion"))
20+
```
21+
22+
## Performance
23+
24+
### Go 1.13.1, Debian 9.1, i7-7700
25+
26+
fixed ua: `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0`
27+
28+
```
29+
go/fixed-8 1279998 927 ns/op 0 B/op 0 allocs/op
30+
cgo/fixed-8 533149 2214 ns/op 224 B/op 19 allocs/op
31+
32+
go/fixed-parallel-8 6197949 195 ns/op 0 B/op 0 allocs/op
33+
cgo/fixed-parallel-8 2119233 566 ns/op 224 B/op 19 allocs/op
34+
35+
go/range-8 422196 2720 ns/op 0 B/op 0 allocs/op
36+
cgo/range-8 400407 2989 ns/op 235 B/op 18 allocs/op
37+
38+
go/range-parallel-8 2709638 437 ns/op 0 B/op 0 allocs/op
39+
cgo/range-parallel-8 1702524 681 ns/op 235 B/op 18 allocs/op
40+
```
41+
42+
## License
43+
44+
[MIT License](LICENSE).

device.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package fiftyonedegrees
2+
3+
type Device struct {
4+
reader *Reader
5+
offset int
6+
}
7+
8+
func (d *Device) GetValue(propertyName string) string {
9+
for i, name := range d.reader.requiredPropertiesNames {
10+
if name == propertyName {
11+
property := d.reader.properties[d.reader.requiredProperties[i]]
12+
profile := d.reader.profiles[d.reader.devices[(d.offset+property.ComponentIndex)*4]+property.SubIndex*4]
13+
return d.reader.strings[profile]
14+
}
15+
}
16+
return ""
17+
}

matcher.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package fiftyonedegrees
2+
3+
type Matcher struct {
4+
reader *Reader
5+
userAgent string
6+
currentNode *Node
7+
hash uint32
8+
power uint32
9+
firstIndex int
10+
lastIndex int
11+
currentIndex int
12+
deviceIndex int
13+
}
14+
15+
func (m *Matcher) getMatchingHashFromListNodeSearch() *NodeHash {
16+
hashes := m.currentNode.Hashes
17+
lower := int32(0)
18+
upper := m.currentNode.HashesCount - 1
19+
middle := int32(0)
20+
for lower <= upper {
21+
middle = lower + (upper-lower)/2
22+
if hashes[middle].HashCode == m.hash {
23+
return &hashes[middle]
24+
} else if hashes[middle].HashCode > m.hash {
25+
upper = middle - 1
26+
} else {
27+
lower = middle + 1
28+
}
29+
}
30+
return nil
31+
}
32+
33+
func (m *Matcher) getMatchingHashFromListNodeTable() *NodeHash {
34+
hashes := m.currentNode.Hashes
35+
index := int(m.hash % m.currentNode.Modulo)
36+
if m.hash == hashes[index].HashCode {
37+
return &hashes[index]
38+
}
39+
if hashes[index].HashCode == 0 && hashes[index].NodeOffset > 0 {
40+
index = int(hashes[index].NodeOffset)
41+
for hashes[index].HashCode != 0 {
42+
if m.hash == hashes[index].HashCode {
43+
return &hashes[index]
44+
}
45+
index++
46+
}
47+
}
48+
return nil
49+
}
50+
51+
func (m *Matcher) getMatchingHashFromListNode() *NodeHash {
52+
if m.currentNode.Modulo == 0 {
53+
return m.getMatchingHashFromListNodeSearch()
54+
}
55+
return m.getMatchingHashFromListNodeTable()
56+
}
57+
58+
func (m *Matcher) advanceHash() bool {
59+
nextAddIndex := 0
60+
if m.currentIndex < m.lastIndex {
61+
nextAddIndex = m.currentIndex + m.currentNode.Length
62+
if nextAddIndex < len(m.userAgent) {
63+
m.hash *= rkPrime
64+
m.hash += uint32(m.userAgent[nextAddIndex])
65+
m.hash -= m.power * uint32(m.userAgent[m.currentIndex])
66+
m.currentIndex++
67+
return true
68+
}
69+
}
70+
return false
71+
}
72+
73+
func (m *Matcher) setInitialHash() bool {
74+
m.hash = 0
75+
if m.firstIndex+m.currentNode.Length <= len(m.userAgent) {
76+
m.power = POWERS[m.currentNode.Length]
77+
for i := m.firstIndex; i < m.firstIndex+m.currentNode.Length; i++ {
78+
m.hash *= rkPrime
79+
m.hash += uint32(m.userAgent[i])
80+
}
81+
m.currentIndex = m.firstIndex
82+
return true
83+
}
84+
return false
85+
}
86+
87+
func (m *Matcher) setNextNode(offset int32) {
88+
if offset > 0 {
89+
index, ok := m.reader.nodeIndexByOffset[int(offset)]
90+
if !ok {
91+
m.currentNode = nil
92+
return
93+
}
94+
m.currentNode = &m.reader.nodes[index]
95+
m.firstIndex += int(m.currentNode.FirstIndex)
96+
m.lastIndex += int(m.currentNode.LastIndex)
97+
} else if offset <= 0 {
98+
m.deviceIndex = -int(offset)
99+
m.currentNode = nil
100+
}
101+
}
102+
103+
func (m *Matcher) evaluateBinaryNode() {
104+
nodeHash := m.currentNode.Hashes[0]
105+
found := false
106+
if m.setInitialHash() {
107+
for m.hash != nodeHash.HashCode && m.advanceHash() {
108+
}
109+
}
110+
if m.hash == nodeHash.HashCode {
111+
found = true
112+
}
113+
if found {
114+
m.setNextNode(nodeHash.NodeOffset)
115+
} else {
116+
m.setNextNode(m.currentNode.UnmatchedNodeOffset)
117+
}
118+
}
119+
120+
func (m *Matcher) evaluateListNode() {
121+
var nodeHash *NodeHash
122+
if m.setInitialHash() {
123+
for {
124+
nodeHash = m.getMatchingHashFromListNode()
125+
if nodeHash != nil || !m.advanceHash() {
126+
break
127+
}
128+
}
129+
}
130+
if nodeHash != nil {
131+
m.setNextNode(nodeHash.NodeOffset)
132+
} else {
133+
m.setNextNode(m.currentNode.UnmatchedNodeOffset)
134+
}
135+
}
136+
137+
func (m *Matcher) Match() Device {
138+
for m.currentNode != nil {
139+
if m.currentNode.HashesCount == 1 {
140+
m.evaluateBinaryNode()
141+
} else {
142+
m.evaluateListNode()
143+
}
144+
}
145+
return Device{
146+
reader: m.reader,
147+
offset: m.deviceIndex * (m.reader.devicePropertiesCount + m.reader.componentsCount),
148+
}
149+
}
150+
151+
func NewMatcher(reader *Reader, userAgent string) Matcher {
152+
return Matcher{
153+
reader: reader,
154+
userAgent: userAgent,
155+
currentNode: &reader.nodes[0],
156+
}
157+
}

0 commit comments

Comments
 (0)