+//go:build !wasm
+// +build !wasm
+
package iplist
import (
"encoding/binary"
+ "fmt"
"io"
"net"
+ "os"
+
+ "github.com/edsrzf/mmap-go"
)
// The packed format is an 8 byte integer of the number of ranges. Then 20
const (
packedRangesOffset = 8
- packedRangeLen = 20
+ packedRangeLen = 44
)
-func (me *IPList) WritePacked(w io.Writer) (err error) {
- descOffsets := make(map[string]int64, len(me.ranges))
- descs := make([]string, 0, len(me.ranges))
+func (ipl *IPList) WritePacked(w io.Writer) (err error) {
+ descOffsets := make(map[string]int64, len(ipl.ranges))
+ descs := make([]string, 0, len(ipl.ranges))
var nextOffset int64
// This is a little monadic, no?
write := func(b []byte, expectedLen int) {
}
}
var b [8]byte
- binary.LittleEndian.PutUint64(b[:], uint64(len(me.ranges)))
+ binary.LittleEndian.PutUint64(b[:], uint64(len(ipl.ranges)))
write(b[:], 8)
- for _, r := range me.ranges {
- write(r.First.To4(), 4)
- write(r.Last.To4(), 4)
+ for _, r := range ipl.ranges {
+ write(r.First.To16(), 16)
+ write(r.Last.To16(), 16)
descOff, ok := descOffsets[r.Description]
if !ok {
descOff = nextOffset
}
func NewFromPacked(b []byte) PackedIPList {
- return PackedIPList(b)
+ ret := PackedIPList(b)
+ minLen := packedRangesOffset + ret.len()*packedRangeLen
+ if len(b) < minLen {
+ panic(fmt.Sprintf("packed len %d < %d", len(b), minLen))
+ }
+ return ret
}
type PackedIPList []byte
var _ Ranger = PackedIPList{}
-func (me PackedIPList) len() int {
- return int(binary.LittleEndian.Uint64(me[:8]))
+func (pil PackedIPList) len() int {
+ return int(binary.LittleEndian.Uint64(pil[:8]))
+}
+
+func (pil PackedIPList) NumRanges() int {
+ return pil.len()
}
-func (me PackedIPList) NumRanges() int {
- return me.len()
+func (pil PackedIPList) getFirst(i int) net.IP {
+ off := packedRangesOffset + packedRangeLen*i
+ return net.IP(pil[off : off+16])
}
-func (me PackedIPList) getRange(i int) (ret Range) {
+func (pil PackedIPList) getRange(i int) (ret Range) {
rOff := packedRangesOffset + packedRangeLen*i
- first := me[rOff : rOff+4]
- last := me[rOff+4 : rOff+8]
- descOff := int(binary.LittleEndian.Uint64(me[rOff+8:]))
- descLen := int(binary.LittleEndian.Uint32(me[rOff+16:]))
- descOff += packedRangesOffset + packedRangeLen*me.len()
- ret = Range{net.IP(first), net.IP(last), string(me[descOff : descOff+descLen])}
+ last := pil[rOff+16 : rOff+32]
+ descOff := int(binary.LittleEndian.Uint64(pil[rOff+32:]))
+ descLen := int(binary.LittleEndian.Uint32(pil[rOff+40:]))
+ descOff += packedRangesOffset + packedRangeLen*pil.len()
+ ret = Range{
+ pil.getFirst(i),
+ net.IP(last),
+ string(pil[descOff : descOff+descLen]),
+ }
return
}
-func (me PackedIPList) Lookup(ip net.IP) (r *Range) {
- ip4 := ip.To4()
- if ip4 == nil {
- // If the IP list was built successfully, then it only contained IPv4
- // ranges. Therefore no IPv6 ranges are blocked.
- if ip.To16() == nil {
- r = &Range{
- Description: "bad IP",
- }
- }
+func (pil PackedIPList) Lookup(ip net.IP) (r Range, ok bool) {
+ ip16 := ip.To16()
+ if ip16 == nil {
+ panic(ip)
+ }
+ return lookup(pil.getFirst, pil.getRange, pil.len(), ip16)
+}
+
+type closerFunc func() error
+
+func (me closerFunc) Close() error {
+ return me()
+}
+
+func MMapPackedFile(filename string) (
+ ret interface {
+ Ranger
+ io.Closer
+ },
+ err error,
+) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ mm, err := mmap.Map(f, mmap.RDONLY, 0)
+ if err != nil {
return
}
- return lookup(me.getRange, me.len(), ip4)
+ ret = struct {
+ Ranger
+ io.Closer
+ }{NewFromPacked(mm), closerFunc(mm.Unmap)}
+ return
}