9 "github.com/edsrzf/mmap-go"
12 // The packed format is an 8 byte integer of the number of ranges. Then 20
13 // bytes per range, consisting of 4 byte packed IP being the lower bound IP of
14 // the range, then 4 bytes of the upper, inclusive bound, 8 bytes for the
15 // offset of the description from the end of the packed ranges, and 4 bytes
16 // for the length of the description. After these packed ranges, are the
17 // concatenated descriptions.
20 packedRangesOffset = 8
24 func (ipl *IPList) WritePacked(w io.Writer) (err error) {
25 descOffsets := make(map[string]int64, len(ipl.ranges))
26 descs := make([]string, 0, len(ipl.ranges))
28 // This is a little monadic, no?
29 write := func(b []byte, expectedLen int) {
43 binary.LittleEndian.PutUint64(b[:], uint64(len(ipl.ranges)))
45 for _, r := range ipl.ranges {
46 write(r.First.To4(), 4)
47 write(r.Last.To4(), 4)
48 descOff, ok := descOffsets[r.Description]
51 descOffsets[r.Description] = descOff
52 descs = append(descs, r.Description)
53 nextOffset += int64(len(r.Description))
55 binary.LittleEndian.PutUint64(b[:], uint64(descOff))
57 binary.LittleEndian.PutUint32(b[:], uint32(len(r.Description)))
60 for _, d := range descs {
61 write([]byte(d), len(d))
66 func NewFromPacked(b []byte) PackedIPList {
67 return PackedIPList(b)
70 type PackedIPList []byte
72 var _ Ranger = PackedIPList{}
74 func (pil PackedIPList) len() int {
75 return int(binary.LittleEndian.Uint64(pil[:8]))
78 func (pil PackedIPList) NumRanges() int {
82 func (pil PackedIPList) getFirst(i int) net.IP {
83 off := packedRangesOffset + packedRangeLen*i
84 return net.IP(pil[off : off+4])
87 func (pil PackedIPList) getRange(i int) (ret Range) {
88 rOff := packedRangesOffset + packedRangeLen*i
89 last := pil[rOff+4 : rOff+8]
90 descOff := int(binary.LittleEndian.Uint64(pil[rOff+8:]))
91 descLen := int(binary.LittleEndian.Uint32(pil[rOff+16:]))
92 descOff += packedRangesOffset + packedRangeLen*pil.len()
96 string(pil[descOff : descOff+descLen]),
101 func (pil PackedIPList) Lookup(ip net.IP) (r Range, ok bool) {
104 // If the IP list was built successfully, then it only contained IPv4
105 // ranges. Therefore no IPv6 ranges are blocked.
106 if ip.To16() == nil {
108 Description: "bad IP",
114 return lookup(pil.getFirst, pil.getRange, pil.len(), ip4)
117 func MMapPacked(filename string) (ret Ranger, err error) {
118 f, err := os.Open(filename)
119 if os.IsNotExist(err) {
127 mm, err := mmap.Map(f, mmap.RDONLY, 0)
131 ret = NewFromPacked(mm)