/packet/repeat.go
package packet
import (
"fmt"
"reflect"
"bytex64.net/code/bitsmash/ac"
)
type RepeatPacket struct {
distance uint8
packetSlice []Packet
}
func (self *RepeatPacket) Length() int {
c := 0
for _, p := range(self.packetSlice) {
c += p.Length()
}
return c
}
func (*RepeatPacket) ByteLength() int {
return 1
}
func (self *RepeatPacket) Encode(encoder ac.CodecManager) {
encoder.Encode(CONTEXT_REPEAT_DISTANCE, uint32(self.distance - 1))
encoder.Encode(CONTEXT_REPEAT_LENGTH, uint32(self.Count() - 1))
}
func DecodeRepeat(decoder ac.CodecManager) Packet {
distance := byte(decoder.Decode(CONTEXT_REPEAT_DISTANCE))
length := byte(decoder.Decode(CONTEXT_REPEAT_LENGTH))
return &RepeatPacket{distance + 1, make([]Packet, length + 1)}
}
func (self *RepeatPacket) Pixels() []uint8 {
pixels := make([]uint8, self.Length())
c := 0
for _, p := range(self.packetSlice) {
pp := p.Pixels()
copy(pixels[c:], pp)
c += len(pp)
}
return pixels
}
func (self *RepeatPacket) String() string {
return fmt.Sprintf("Repeat distance %d length %d", self.distance, len(self.packetSlice))
}
func (self *RepeatPacket) Count() int {
return len(self.packetSlice)
}
func (self *RepeatPacket) Distance() int {
return int(self.distance)
}
func (self *RepeatPacket) Slice() []Packet {
return self.packetSlice
}
func (self *RepeatPacket) UnRepeat(packets []Packet, head int) []Packet {
count := self.Count()
start := head - int(self.distance)
for i := range(self.packetSlice) {
packets = append(packets, packets[start + i])
}
self.packetSlice = packets[start:start + count]
return packets
}
type packetMatch struct {
Start int
Length int
}
func findMatch(packets []Packet, head int, start int) *packetMatch {
matchlen := 0
maxlen := len(packets) - head
if maxlen > 8 {
maxlen = 8
}
for ; matchlen < maxlen && reflect.DeepEqual(packets[start + matchlen], packets[head + matchlen]); matchlen++ {
}
if matchlen == 0 {
return nil
}
headbytelen := packets[head].ByteLength()
if headbytelen == 1 && matchlen < 2 {
// Not saving any space for a single byte packet if we're not matching
// at least two packets
return nil
}
return &packetMatch{start, matchlen}
}
func ScanRepeat(packets []Packet, head int) Packet {
if head == 0 {
// Won't find any repeats at the head of the list
return nil
}
start := head - 32
if start < 0 {
start = 0
}
var bestMatch *packetMatch
for i := head - 1; i >= start; i-- {
match := findMatch(packets, head, i)
if match == nil {
continue
}
if bestMatch == nil || match.Length > bestMatch.Length {
bestMatch = match
}
}
if (bestMatch == nil) {
return nil
}
return &RepeatPacket{uint8(head - bestMatch.Start), packets[bestMatch.Start:bestMatch.Start + bestMatch.Length]}
}