mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-06 22:18:28 +00:00
switch to packaged lzma package
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of the author nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
BIN
Binary file not shown.
+3982
File diff suppressed because it is too large
Load Diff
+355
@@ -0,0 +1,355 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import "io"
|
||||
|
||||
const (
|
||||
kHash2Size = 1 << 10
|
||||
kHash3Size = 1 << 16
|
||||
kBT2HashSize = 1 << 16
|
||||
kStartMaxLen = 1
|
||||
kHash3Offset = kHash2Size
|
||||
kEmptyHashValue = 0
|
||||
kMaxValForNormalize = (1 << 30) - 1
|
||||
)
|
||||
|
||||
type lzBinTree struct {
|
||||
iw *lzInWindow
|
||||
son []uint32
|
||||
hash []uint32
|
||||
cyclicBufPos uint32
|
||||
cyclicBufSize uint32
|
||||
matchMaxLen uint32
|
||||
cutValue uint32
|
||||
hashMask uint32
|
||||
hashSizeSum uint32
|
||||
kvNumHashDirectBytes uint32
|
||||
kvMinMatchCheck uint32
|
||||
kvFixHashSize uint32
|
||||
hashArray bool
|
||||
}
|
||||
|
||||
func newLzBinTree(r io.Reader, historySize, keepAddBufBefore, matchMaxLen, keepAddBufAfter, numHashBytes uint32) *lzBinTree {
|
||||
bt := &lzBinTree{
|
||||
son: make([]uint32, (historySize+1)*2), // history size is the dictSize from the encoder
|
||||
cyclicBufPos: 0,
|
||||
cyclicBufSize: historySize + 1,
|
||||
matchMaxLen: matchMaxLen,
|
||||
cutValue: 16 + (matchMaxLen >> 1),
|
||||
}
|
||||
|
||||
winSizeReserv := (historySize+keepAddBufBefore+matchMaxLen+keepAddBufAfter)/2 + 256
|
||||
bt.iw = newLzInWindow(r, historySize+keepAddBufBefore, matchMaxLen+keepAddBufAfter, winSizeReserv)
|
||||
|
||||
if numHashBytes > 2 {
|
||||
bt.hashArray = true
|
||||
bt.kvNumHashDirectBytes = 0
|
||||
bt.kvMinMatchCheck = 4
|
||||
bt.kvFixHashSize = kHash2Size + kHash3Size
|
||||
} else {
|
||||
bt.hashArray = false
|
||||
bt.kvNumHashDirectBytes = 2
|
||||
bt.kvMinMatchCheck = 3
|
||||
bt.kvFixHashSize = 0
|
||||
}
|
||||
|
||||
hs := uint32(kBT2HashSize)
|
||||
if bt.hashArray == true {
|
||||
hs = historySize - 1
|
||||
hs |= hs >> 1
|
||||
hs |= hs >> 2
|
||||
hs |= hs >> 4
|
||||
hs |= hs >> 8
|
||||
hs >>= 1
|
||||
hs |= 0xFFFF
|
||||
if hs > 1<<24 {
|
||||
hs >>= 1
|
||||
}
|
||||
bt.hashMask = hs
|
||||
hs++
|
||||
hs += bt.kvFixHashSize
|
||||
}
|
||||
bt.hashSizeSum = hs
|
||||
bt.hash = make([]uint32, bt.hashSizeSum)
|
||||
for i := uint32(0); i < bt.hashSizeSum; i++ {
|
||||
bt.hash[i] = kEmptyHashValue
|
||||
}
|
||||
|
||||
bt.iw.reduceOffsets(0xFFFFFFFF)
|
||||
return bt
|
||||
}
|
||||
|
||||
func normalizeLinks(items []uint32, numItems, subValue uint32) {
|
||||
for i := uint32(0); i < numItems; i++ {
|
||||
value := items[i]
|
||||
if value <= subValue {
|
||||
value = kEmptyHashValue
|
||||
} else {
|
||||
value -= subValue
|
||||
}
|
||||
items[i] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (bt *lzBinTree) normalize() {
|
||||
subValue := bt.iw.pos - bt.cyclicBufSize
|
||||
normalizeLinks(bt.son, bt.cyclicBufSize*2, subValue)
|
||||
normalizeLinks(bt.hash, bt.hashSizeSum, subValue)
|
||||
bt.iw.reduceOffsets(subValue)
|
||||
}
|
||||
|
||||
func (bt *lzBinTree) movePos() {
|
||||
bt.cyclicBufPos++
|
||||
if bt.cyclicBufPos >= bt.cyclicBufSize {
|
||||
bt.cyclicBufPos = 0
|
||||
}
|
||||
bt.iw.movePos()
|
||||
if bt.iw.pos == kMaxValForNormalize {
|
||||
bt.normalize()
|
||||
}
|
||||
}
|
||||
|
||||
func (bt *lzBinTree) getMatches(distances []uint32) uint32 {
|
||||
var lenLimit uint32
|
||||
if bt.iw.pos+bt.matchMaxLen <= bt.iw.streamPos {
|
||||
lenLimit = bt.matchMaxLen
|
||||
} else {
|
||||
lenLimit = bt.iw.streamPos - bt.iw.pos
|
||||
if lenLimit < bt.kvMinMatchCheck {
|
||||
bt.movePos()
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
offset := uint32(0)
|
||||
matchMinPos := uint32(0)
|
||||
if bt.iw.pos > bt.cyclicBufSize {
|
||||
matchMinPos = bt.iw.pos - bt.cyclicBufSize
|
||||
}
|
||||
cur := bt.iw.bufOffset + bt.iw.pos
|
||||
maxLen := uint32(kStartMaxLen)
|
||||
var hashValue uint32
|
||||
hash2Value := uint32(0)
|
||||
hash3Value := uint32(0)
|
||||
|
||||
if bt.hashArray == true {
|
||||
tmp := crcTable[bt.iw.buf[cur]] ^ uint32(bt.iw.buf[cur+1])
|
||||
hash2Value = tmp & (kHash2Size - 1)
|
||||
tmp ^= uint32(bt.iw.buf[cur+2]) << 8
|
||||
hash3Value = tmp & (kHash3Size - 1)
|
||||
hashValue = (tmp ^ crcTable[bt.iw.buf[cur+3]]<<5) & bt.hashMask
|
||||
} else {
|
||||
hashValue = uint32(bt.iw.buf[cur]) ^ uint32(bt.iw.buf[cur+1])<<8
|
||||
}
|
||||
|
||||
curMatch := bt.hash[bt.kvFixHashSize+hashValue]
|
||||
if bt.hashArray == true {
|
||||
curMatch2 := bt.hash[hash2Value]
|
||||
curMatch3 := bt.hash[kHash3Offset+hash3Value]
|
||||
bt.hash[hash2Value] = bt.iw.pos
|
||||
bt.hash[kHash3Offset+hash3Value] = bt.iw.pos
|
||||
if curMatch2 > matchMinPos {
|
||||
if bt.iw.buf[bt.iw.bufOffset+curMatch2] == bt.iw.buf[cur] {
|
||||
maxLen = 2
|
||||
distances[offset] = maxLen
|
||||
offset++
|
||||
distances[offset] = bt.iw.pos - curMatch2 - 1
|
||||
offset++
|
||||
}
|
||||
}
|
||||
if curMatch3 > matchMinPos {
|
||||
if bt.iw.buf[bt.iw.bufOffset+curMatch3] == bt.iw.buf[cur] {
|
||||
if curMatch3 == curMatch2 {
|
||||
offset -= 2
|
||||
}
|
||||
maxLen = 3
|
||||
distances[offset] = maxLen
|
||||
offset++
|
||||
distances[offset] = bt.iw.pos - curMatch3 - 1
|
||||
offset++
|
||||
curMatch2 = curMatch3
|
||||
}
|
||||
}
|
||||
if offset != 0 && curMatch2 == curMatch {
|
||||
offset -= 2
|
||||
maxLen = kStartMaxLen
|
||||
}
|
||||
}
|
||||
|
||||
bt.hash[bt.kvFixHashSize+hashValue] = bt.iw.pos
|
||||
|
||||
if bt.kvNumHashDirectBytes != 0 {
|
||||
if curMatch > matchMinPos {
|
||||
if bt.iw.buf[bt.iw.bufOffset+curMatch+bt.kvNumHashDirectBytes] != bt.iw.buf[cur+bt.kvNumHashDirectBytes] {
|
||||
maxLen = bt.kvNumHashDirectBytes
|
||||
distances[offset] = maxLen
|
||||
offset++
|
||||
distances[offset] = bt.iw.pos - curMatch - 1
|
||||
offset++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr0 := bt.cyclicBufPos<<1 + 1
|
||||
ptr1 := bt.cyclicBufPos << 1
|
||||
len0 := bt.kvNumHashDirectBytes
|
||||
len1 := bt.kvNumHashDirectBytes
|
||||
count := bt.cutValue
|
||||
|
||||
for {
|
||||
if curMatch <= matchMinPos || count == 0 {
|
||||
bt.son[ptr1] = kEmptyHashValue
|
||||
bt.son[ptr0] = kEmptyHashValue
|
||||
break
|
||||
}
|
||||
count--
|
||||
|
||||
delta := bt.iw.pos - curMatch
|
||||
var cyclicPos uint32
|
||||
if delta <= bt.cyclicBufPos {
|
||||
cyclicPos = (bt.cyclicBufPos - delta) << 1
|
||||
} else {
|
||||
cyclicPos = (bt.cyclicBufPos - delta + bt.cyclicBufSize) << 1
|
||||
}
|
||||
pby1 := bt.iw.bufOffset + curMatch
|
||||
length := minUInt32(len0, len1)
|
||||
if bt.iw.buf[pby1+length] == bt.iw.buf[cur+length] {
|
||||
for length++; length != lenLimit; length++ {
|
||||
if bt.iw.buf[pby1+length] != bt.iw.buf[cur+length] {
|
||||
break
|
||||
}
|
||||
}
|
||||
if maxLen < length {
|
||||
maxLen = length
|
||||
distances[offset] = maxLen
|
||||
offset++
|
||||
distances[offset] = delta - 1
|
||||
offset++
|
||||
if length == lenLimit {
|
||||
bt.son[ptr1] = bt.son[cyclicPos]
|
||||
bt.son[ptr0] = bt.son[cyclicPos+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bt.iw.buf[pby1+length] < bt.iw.buf[cur+length] {
|
||||
bt.son[ptr1] = curMatch
|
||||
ptr1 = cyclicPos + 1
|
||||
curMatch = bt.son[ptr1]
|
||||
len1 = length
|
||||
} else {
|
||||
bt.son[ptr0] = curMatch
|
||||
ptr0 = cyclicPos
|
||||
curMatch = bt.son[ptr0]
|
||||
len0 = length
|
||||
}
|
||||
}
|
||||
bt.movePos()
|
||||
return offset
|
||||
}
|
||||
|
||||
func (bt *lzBinTree) skip(num uint32) {
|
||||
for i := uint32(0); i < num; i++ {
|
||||
var lenLimit uint32
|
||||
if bt.iw.pos+bt.matchMaxLen <= bt.iw.streamPos {
|
||||
lenLimit = bt.matchMaxLen
|
||||
} else {
|
||||
lenLimit = bt.iw.streamPos - bt.iw.pos
|
||||
if lenLimit < bt.kvMinMatchCheck {
|
||||
bt.movePos()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
matchMinPos := uint32(0)
|
||||
if bt.iw.pos > bt.cyclicBufSize {
|
||||
matchMinPos = bt.iw.pos - bt.cyclicBufSize
|
||||
}
|
||||
cur := bt.iw.bufOffset + bt.iw.pos
|
||||
var hashValue uint32
|
||||
if bt.hashArray == true {
|
||||
tmp := crcTable[bt.iw.buf[cur]] ^ uint32(bt.iw.buf[cur+1])
|
||||
hash2Value := tmp & (kHash2Size - 1)
|
||||
bt.hash[hash2Value] = bt.iw.pos
|
||||
tmp ^= uint32(bt.iw.buf[cur+2]) << 8
|
||||
hash3Value := tmp & (kHash3Size - 1)
|
||||
bt.hash[kHash3Offset+hash3Value] = bt.iw.pos
|
||||
hashValue = (tmp ^ crcTable[bt.iw.buf[cur+3]]<<5) & bt.hashMask
|
||||
} else {
|
||||
hashValue = uint32(bt.iw.buf[cur]) ^ uint32(bt.iw.buf[cur+1])<<8
|
||||
}
|
||||
|
||||
curMatch := bt.hash[bt.kvFixHashSize+hashValue]
|
||||
bt.hash[bt.kvFixHashSize+hashValue] = bt.iw.pos
|
||||
ptr0 := bt.cyclicBufPos<<1 + 1
|
||||
ptr1 := bt.cyclicBufPos << 1
|
||||
len0 := bt.kvNumHashDirectBytes
|
||||
len1 := bt.kvNumHashDirectBytes
|
||||
count := bt.cutValue
|
||||
for {
|
||||
if curMatch <= matchMinPos || count == 0 {
|
||||
bt.son[ptr1] = kEmptyHashValue
|
||||
bt.son[ptr0] = kEmptyHashValue
|
||||
break
|
||||
}
|
||||
count--
|
||||
|
||||
delta := bt.iw.pos - curMatch
|
||||
var cyclicPos uint32
|
||||
if delta <= bt.cyclicBufPos {
|
||||
cyclicPos = (bt.cyclicBufPos - delta) << 1
|
||||
} else {
|
||||
cyclicPos = (bt.cyclicBufPos - delta + bt.cyclicBufSize) << 1
|
||||
}
|
||||
pby1 := bt.iw.bufOffset + curMatch
|
||||
length := minUInt32(len0, len1)
|
||||
if bt.iw.buf[pby1+length] == bt.iw.buf[cur+length] {
|
||||
for length++; length != lenLimit; length++ {
|
||||
if bt.iw.buf[pby1+length] != bt.iw.buf[cur+length] {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length == lenLimit {
|
||||
bt.son[ptr1] = bt.son[cyclicPos]
|
||||
bt.son[ptr0] = bt.son[cyclicPos+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if bt.iw.buf[pby1+length] < bt.iw.buf[cur+length] {
|
||||
bt.son[ptr1] = curMatch
|
||||
ptr1 = cyclicPos + 1
|
||||
curMatch = bt.son[ptr1]
|
||||
len1 = length
|
||||
} else {
|
||||
bt.son[ptr0] = curMatch
|
||||
ptr0 = cyclicPos
|
||||
curMatch = bt.son[ptr0]
|
||||
len0 = length
|
||||
}
|
||||
}
|
||||
bt.movePos()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var crcTable []uint32 = make([]uint32, 256)
|
||||
|
||||
// should be called in the encoder's contructor
|
||||
func initCrcTable() {
|
||||
for i := uint32(0); i < 256; i++ {
|
||||
r := i
|
||||
for j := 0; j < 8; j++ {
|
||||
if r&1 != 0 {
|
||||
r = r>>1 ^ 0xEDB88320
|
||||
} else {
|
||||
r >>= 1
|
||||
}
|
||||
}
|
||||
crcTable[i] = r
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import "io"
|
||||
|
||||
type lzOutWindow struct {
|
||||
w io.Writer
|
||||
buf []byte
|
||||
winSize uint32
|
||||
pos uint32
|
||||
streamPos uint32
|
||||
//unpacked uint32 // counter of unpacked bytes
|
||||
}
|
||||
|
||||
func newLzOutWindow(w io.Writer, windowSize uint32) *lzOutWindow {
|
||||
return &lzOutWindow{
|
||||
w: w,
|
||||
buf: make([]byte, windowSize),
|
||||
winSize: windowSize,
|
||||
pos: 0,
|
||||
streamPos: 0,
|
||||
//unpacked: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (ow *lzOutWindow) flush() {
|
||||
size := ow.pos - ow.streamPos
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
n, err := ow.w.Write(ow.buf[ow.streamPos : ow.streamPos+size])
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
if uint32(n) != size {
|
||||
throw(nWriteError)
|
||||
}
|
||||
//unpacked += size
|
||||
if ow.pos >= ow.winSize {
|
||||
ow.pos = 0
|
||||
}
|
||||
ow.streamPos = ow.pos
|
||||
}
|
||||
|
||||
func (ow *lzOutWindow) copyBlock(distance, length uint32) {
|
||||
pos := ow.pos - distance - 1
|
||||
if pos >= ow.winSize {
|
||||
pos += ow.winSize
|
||||
}
|
||||
for ; length != 0; length-- {
|
||||
if pos >= ow.winSize {
|
||||
pos = 0
|
||||
}
|
||||
ow.buf[ow.pos] = ow.buf[pos]
|
||||
ow.pos++
|
||||
pos++
|
||||
if ow.pos >= ow.winSize {
|
||||
ow.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ow *lzOutWindow) putByte(b byte) {
|
||||
ow.buf[ow.pos] = b
|
||||
ow.pos++
|
||||
if ow.pos >= ow.winSize {
|
||||
ow.flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (ow *lzOutWindow) getByte(distance uint32) byte {
|
||||
pos := ow.pos - distance - 1
|
||||
if pos >= ow.winSize {
|
||||
pos += ow.winSize
|
||||
}
|
||||
return ow.buf[pos]
|
||||
}
|
||||
|
||||
type lzInWindow struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
posLimit uint32
|
||||
lastSafePos uint32
|
||||
bufOffset uint32
|
||||
blockSize uint32
|
||||
pos uint32
|
||||
keepSizeBefore uint32
|
||||
keepSizeAfter uint32
|
||||
streamPos uint32
|
||||
streamEnd bool
|
||||
}
|
||||
|
||||
func newLzInWindow(r io.Reader, keepSizeBefore, keepSizeAfter, keepSizeReserv uint32) *lzInWindow {
|
||||
blockSize := keepSizeBefore + keepSizeAfter + keepSizeReserv
|
||||
iw := &lzInWindow{
|
||||
r: r,
|
||||
buf: make([]byte, blockSize),
|
||||
lastSafePos: blockSize - keepSizeAfter,
|
||||
bufOffset: 0,
|
||||
blockSize: blockSize,
|
||||
pos: 0,
|
||||
keepSizeBefore: keepSizeBefore,
|
||||
keepSizeAfter: keepSizeAfter,
|
||||
streamPos: 0,
|
||||
streamEnd: false,
|
||||
}
|
||||
iw.readBlock()
|
||||
return iw
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) moveBlock() {
|
||||
offset := iw.bufOffset + iw.pos - iw.keepSizeBefore
|
||||
if offset > 0 {
|
||||
offset--
|
||||
}
|
||||
numBytes := iw.bufOffset + iw.streamPos - offset
|
||||
for i := uint32(0); i < numBytes; i++ {
|
||||
iw.buf[i] = iw.buf[offset+i]
|
||||
}
|
||||
iw.bufOffset -= offset
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) readBlock() {
|
||||
if iw.streamEnd {
|
||||
return
|
||||
}
|
||||
for {
|
||||
if iw.blockSize-iw.bufOffset-iw.streamPos == 0 {
|
||||
return
|
||||
}
|
||||
n, err := iw.r.Read(iw.buf[iw.bufOffset+iw.streamPos : iw.blockSize])
|
||||
if err != nil && err != io.EOF {
|
||||
throw(err)
|
||||
}
|
||||
if n == 0 && err == io.EOF {
|
||||
iw.posLimit = iw.streamPos
|
||||
ptr := iw.bufOffset + iw.posLimit
|
||||
if ptr > iw.lastSafePos {
|
||||
iw.posLimit = iw.lastSafePos - iw.bufOffset
|
||||
}
|
||||
iw.streamEnd = true
|
||||
return
|
||||
}
|
||||
iw.streamPos += uint32(n)
|
||||
if iw.streamPos >= iw.pos+iw.keepSizeAfter {
|
||||
iw.posLimit = iw.streamPos - iw.keepSizeAfter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) movePos() {
|
||||
iw.pos++
|
||||
if iw.pos > iw.posLimit {
|
||||
ptr := iw.bufOffset + iw.pos
|
||||
if ptr > iw.lastSafePos {
|
||||
iw.moveBlock()
|
||||
}
|
||||
iw.readBlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) getIndexByte(index int32) byte {
|
||||
return iw.buf[int32(iw.bufOffset+iw.pos)+index]
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) getMatchLen(index int32, distance, limit uint32) (res uint32) {
|
||||
uIndex := uint32(index)
|
||||
if iw.streamEnd == true {
|
||||
if iw.pos+uIndex+limit > iw.streamPos {
|
||||
limit = iw.streamPos - (iw.pos + uIndex)
|
||||
}
|
||||
}
|
||||
distance++
|
||||
pby := iw.bufOffset + iw.pos + uIndex
|
||||
for res = uint32(0); res < limit && iw.buf[pby+res] == iw.buf[pby+res-distance]; res++ {
|
||||
// empty body
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) getNumAvailableBytes() uint32 {
|
||||
return iw.streamPos - iw.pos
|
||||
}
|
||||
|
||||
func (iw *lzInWindow) reduceOffsets(subValue uint32) {
|
||||
iw.bufOffset += subValue
|
||||
iw.posLimit -= subValue
|
||||
iw.pos -= subValue
|
||||
iw.streamPos -= subValue
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
type lzmaTest struct {
|
||||
descr string
|
||||
level int
|
||||
size bool
|
||||
raw string
|
||||
lzma []byte
|
||||
err error
|
||||
}
|
||||
|
||||
var lzmaTests = []lzmaTest{
|
||||
// fmt -w 80 -s file
|
||||
// cat file | sed s/\\\\/\\\\\\\\/g | sed s/\"/\\\\\"/g | sed s/^/\"/ | sed s/$/\\\\n\"\ +/
|
||||
// hexdump -Cv file.lzma | awk '{for (i=2; i<18; i++) {printf("0x%s, ", $i); if (i==9) printf("\n");} printf("\n")}'
|
||||
lzmaTest{
|
||||
"empty test",
|
||||
3,
|
||||
true,
|
||||
"",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"empty (with size == -1) test",
|
||||
3,
|
||||
false,
|
||||
"",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, 0xff,
|
||||
0xfb, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"hello world test",
|
||||
3,
|
||||
true,
|
||||
"hello world\n",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x19,
|
||||
0x49, 0xee, 0x8d, 0xe9, 0x17, 0x89, 0x3a, 0x33,
|
||||
0x5f, 0xfc, 0xac, 0xf7, 0x20, 0x00,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"hello world (with size == -1) test",
|
||||
3,
|
||||
false,
|
||||
"hello world\n",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x34, 0x19,
|
||||
0x49, 0xee, 0x8d, 0xe9, 0x17, 0x89, 0x3a, 0x33,
|
||||
0x5f, 0xfc, 0xb2, 0x09, 0x82, 0x2f, 0xff, 0xfd,
|
||||
0xe2, 0x80, 0x00,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"text test",
|
||||
3,
|
||||
true,
|
||||
"Two named types are identical if their type names originate in the same type\n" +
|
||||
"declaration (§Declarations and scope). A named and an unnamed type are never\n" +
|
||||
"identical. Two unnamed types are identical if the corresponding type literals\n" +
|
||||
"have the same literal structure and corresponding components have identical\n" +
|
||||
"types. In detail:\n" +
|
||||
"\n" +
|
||||
" * Two array types are identical if they have identical element types and\n" +
|
||||
"the same array length.\n" +
|
||||
" * Two slice types are identical if they have identical element types.\n" +
|
||||
" * Two struct types are identical if they have the same sequence of fields,\n" +
|
||||
"and if corresponding fields have the same names and identical types. Two\n" +
|
||||
"anonymous fields are considered to have the same name.\n" +
|
||||
" * Two pointer types are identical if they have identical base types.\n" +
|
||||
" * Two function types are identical if they have the same number of\n" +
|
||||
"parameters and result values and if corresponding parameter and result types\n" +
|
||||
"are identical. All \"...\" parameters without a specified type are defined to\n" +
|
||||
"have identical type. All \"...\" parameters with specified identical type T are\n" +
|
||||
"defined to have identical type. Parameter and result names are not required to\n" +
|
||||
"match.\n" +
|
||||
" * Two interface types are identical if they have the same set of methods\n" +
|
||||
"with the same names and identical function types. The order of the methods is\n" +
|
||||
"irrelevant.\n" +
|
||||
" * Two map types are identical if they have identical key and value types.\n" +
|
||||
" * Two channel types are identical if they have identical value types and\n" +
|
||||
"the same direction.\n",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0xe8, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x1d,
|
||||
0xc9, 0xe2, 0x03, 0x0c, 0x4e, 0x75, 0xc8, 0xee,
|
||||
0x65, 0x97, 0xae, 0x0a, 0x7b, 0x0a, 0x66, 0xfa,
|
||||
0x4a, 0x94, 0xe6, 0x32, 0x2f, 0x9b, 0x44, 0x8c,
|
||||
0xaf, 0xf6, 0xf4, 0x67, 0xe2, 0x8a, 0x8b, 0x2f,
|
||||
0x26, 0x41, 0xf6, 0x64, 0xe2, 0xf6, 0x00, 0xbb,
|
||||
0x1f, 0x4a, 0xb1, 0xca, 0xa6, 0xf0, 0x54, 0x9e,
|
||||
0xa5, 0x2d, 0xe7, 0x6e, 0x6a, 0x49, 0x84, 0xac,
|
||||
0xf5, 0x52, 0xcf, 0x57, 0xcd, 0xa7, 0x84, 0xe9,
|
||||
0x27, 0xdc, 0x3d, 0xf3, 0xf4, 0x1a, 0x8d, 0x98,
|
||||
0xba, 0xcc, 0x47, 0xca, 0x1b, 0x38, 0xa6, 0x8c,
|
||||
0x96, 0x24, 0x14, 0x8d, 0x1c, 0x81, 0x6b, 0xc0,
|
||||
0x1c, 0x69, 0xa1, 0x79, 0xa2, 0x20, 0x10, 0x6c,
|
||||
0x0c, 0x56, 0x40, 0x1f, 0x3f, 0x32, 0x4a, 0x4f,
|
||||
0x1f, 0x2a, 0x29, 0xf0, 0x23, 0x99, 0xbe, 0xc7,
|
||||
0x49, 0x7e, 0xc7, 0x60, 0xa3, 0x54, 0x9c, 0x06,
|
||||
0x06, 0xb4, 0xc4, 0x4d, 0x07, 0xd5, 0xef, 0xa0,
|
||||
0xcd, 0xac, 0x8f, 0x54, 0xf9, 0x15, 0x15, 0xa0,
|
||||
0xb5, 0xf1, 0x7d, 0xde, 0xeb, 0xe0, 0x41, 0x9a,
|
||||
0x3e, 0x5a, 0x66, 0xd2, 0xfc, 0x08, 0x00, 0xe8,
|
||||
0xf2, 0xc9, 0x04, 0x94, 0x7e, 0x0b, 0x06, 0x56,
|
||||
0x9d, 0xe7, 0x1f, 0x48, 0x21, 0x4d, 0x18, 0x21,
|
||||
0xa3, 0x5f, 0x69, 0x7a, 0xed, 0x4d, 0x2d, 0xb4,
|
||||
0x3e, 0x34, 0xcf, 0x9e, 0xae, 0x11, 0x2d, 0x15,
|
||||
0xbc, 0x2c, 0xd7, 0xef, 0x1c, 0xd5, 0xd8, 0xd3,
|
||||
0xc2, 0x4f, 0xcb, 0x2c, 0xb1, 0xa3, 0x85, 0x0a,
|
||||
0xad, 0x44, 0x68, 0x42, 0xab, 0x40, 0x58, 0xa2,
|
||||
0x9b, 0xdf, 0xbd, 0xa2, 0x10, 0xc7, 0x2e, 0x5a,
|
||||
0x1d, 0x07, 0xe4, 0xb5, 0xa6, 0xa8, 0xb3, 0x12,
|
||||
0x44, 0x64, 0x18, 0xaf, 0xa7, 0x72, 0x05, 0x29,
|
||||
0x48, 0xc6, 0x78, 0xef, 0x7f, 0x54, 0xe9, 0xb3,
|
||||
0xae, 0x10, 0x51, 0x11, 0x91, 0xf6, 0x96, 0x40,
|
||||
0x6a, 0xf1, 0xc8, 0x3d, 0x46, 0x86, 0x2e, 0xd3,
|
||||
0xa1, 0xdc, 0x38, 0x97, 0x11, 0x49, 0x4b, 0x03,
|
||||
0x91, 0xa5, 0xed, 0x53, 0x3f, 0xd5, 0x87, 0x04,
|
||||
0x25, 0xf0, 0xe2, 0x19, 0x55, 0x99, 0x6b, 0xad,
|
||||
0xa2, 0x9c, 0x7a, 0xcd, 0xc9, 0x34, 0x18, 0x8f,
|
||||
0x4c, 0xaa, 0xed, 0xd3, 0x49, 0x98, 0xd9, 0x67,
|
||||
0xbe, 0x41, 0x85, 0xbf, 0x09, 0x22, 0x16, 0xa0,
|
||||
0x81, 0x71, 0x38, 0xdb, 0xb2, 0xd6, 0xfe, 0x2b,
|
||||
0xf4, 0x03, 0xf8, 0xd1, 0x00, 0x16, 0x5e, 0x77,
|
||||
0x69, 0xd4, 0x28, 0x8f, 0x94, 0x4d, 0x58, 0x4f,
|
||||
0xae, 0x6b, 0xb1, 0x09, 0x85, 0x71, 0xd7, 0x3a,
|
||||
0x4b, 0xea, 0xd2, 0x70, 0xbb, 0xa2, 0x20, 0x85,
|
||||
0x2d, 0xbd, 0x57, 0x3f, 0x81, 0x3e, 0xe4, 0xa2,
|
||||
0x43, 0x3e, 0xee, 0x04, 0xbe, 0x42, 0x51, 0xaa,
|
||||
0xad, 0xda, 0x53, 0x87, 0xeb, 0xc0, 0x9a, 0xde,
|
||||
0xa2, 0x7e, 0x19, 0x4f, 0xa7, 0xdf, 0x23, 0x96,
|
||||
0xdd, 0xe3, 0x18, 0x0f, 0xc0, 0x48, 0xfd, 0x9f,
|
||||
0x13, 0xc3, 0x3c, 0x3a, 0x7b, 0x7c, 0xa8, 0x89,
|
||||
0x6a, 0xd6, 0x68, 0xdd, 0x3b, 0xc6, 0xc6, 0x44,
|
||||
0x79, 0xc5, 0x59, 0x1c, 0x23, 0xa9, 0x8b, 0x00,
|
||||
0x62, 0xeb, 0x3a, 0x51, 0x14, 0x5e, 0x63, 0x79,
|
||||
0x97, 0x60, 0x62, 0x13, 0x49, 0x41, 0x06, 0xf4,
|
||||
0x5f, 0xe7, 0x9b, 0xd6, 0x51, 0x31, 0x6e, 0x7d,
|
||||
0x5e, 0xe8, 0x72, 0xce, 0x5e, 0xd0, 0xa7, 0x9d,
|
||||
0xa3, 0xa9, 0x92, 0xa8, 0x2f, 0x78, 0x00, 0x92,
|
||||
0x4f, 0xf0, 0x21, 0xb6, 0x74, 0xfb, 0x3c, 0xe2,
|
||||
0x60, 0xdf, 0x82, 0x09, 0x07, 0xb3, 0x68, 0x5b,
|
||||
0xe2, 0x49, 0xeb, 0x81, 0x98, 0x23, 0x19, 0xdc,
|
||||
0x2c, 0xa1, 0xad, 0x95, 0xc0, 0x9a, 0x48, 0xcd,
|
||||
0xa8, 0xe4, 0xdf, 0xbb, 0xca, 0xd4, 0x47, 0x46,
|
||||
0xe2, 0xdb, 0x5c, 0xe2, 0xdb, 0xb2, 0x32, 0x44,
|
||||
0x87, 0x0a, 0x49, 0x99, 0xc6, 0x32, 0x75, 0xc6,
|
||||
0xe7, 0x1b, 0xf1, 0x71, 0x74, 0x32, 0x4a, 0x17,
|
||||
0x2f, 0xe8, 0x00,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"text test with size == -1",
|
||||
3,
|
||||
false,
|
||||
"Two named types are identical if their type names originate in the same type\n" +
|
||||
"declaration (§Declarations and scope). A named and an unnamed type are never\n" +
|
||||
"identical. Two unnamed types are identical if the corresponding type literals\n" +
|
||||
"have the same literal structure and corresponding components have identical\n" +
|
||||
"types. In detail:\n" +
|
||||
"\n" +
|
||||
" * Two array types are identical if they have identical element types and\n" +
|
||||
"the same array length.\n" +
|
||||
" * Two slice types are identical if they have identical element types.\n" +
|
||||
" * Two struct types are identical if they have the same sequence of fields,\n" +
|
||||
"and if corresponding fields have the same names and identical types. Two\n" +
|
||||
"anonymous fields are considered to have the same name.\n" +
|
||||
" * Two pointer types are identical if they have identical base types.\n" +
|
||||
" * Two function types are identical if they have the same number of\n" +
|
||||
"parameters and result values and if corresponding parameter and result types\n" +
|
||||
"are identical. All \"...\" parameters without a specified type are defined to\n" +
|
||||
"have identical type. All \"...\" parameters with specified identical type T are\n" +
|
||||
"defined to have identical type. Parameter and result names are not required to\n" +
|
||||
"match.\n" +
|
||||
" * Two interface types are identical if they have the same set of methods\n" +
|
||||
"with the same names and identical function types. The order of the methods is\n" +
|
||||
"irrelevant.\n" +
|
||||
" * Two map types are identical if they have identical key and value types.\n" +
|
||||
" * Two channel types are identical if they have identical value types and\n" +
|
||||
"the same direction.\n",
|
||||
[]byte{
|
||||
0x5d, 0x00, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x2a, 0x1d,
|
||||
0xc9, 0xe2, 0x03, 0x0c, 0x4e, 0x75, 0xc8, 0xee,
|
||||
0x65, 0x97, 0xae, 0x0a, 0x7b, 0x0a, 0x66, 0xfa,
|
||||
0x4a, 0x94, 0xe6, 0x32, 0x2f, 0x9b, 0x44, 0x8c,
|
||||
0xaf, 0xf6, 0xf4, 0x67, 0xe2, 0x8a, 0x8b, 0x2f,
|
||||
0x26, 0x41, 0xf6, 0x64, 0xe2, 0xf6, 0x00, 0xbb,
|
||||
0x1f, 0x4a, 0xb1, 0xca, 0xa6, 0xf0, 0x54, 0x9e,
|
||||
0xa5, 0x2d, 0xe7, 0x6e, 0x6a, 0x49, 0x84, 0xac,
|
||||
0xf5, 0x52, 0xcf, 0x57, 0xcd, 0xa7, 0x84, 0xe9,
|
||||
0x27, 0xdc, 0x3d, 0xf3, 0xf4, 0x1a, 0x8d, 0x98,
|
||||
0xba, 0xcc, 0x47, 0xca, 0x1b, 0x38, 0xa6, 0x8c,
|
||||
0x96, 0x24, 0x14, 0x8d, 0x1c, 0x81, 0x6b, 0xc0,
|
||||
0x1c, 0x69, 0xa1, 0x79, 0xa2, 0x20, 0x10, 0x6c,
|
||||
0x0c, 0x56, 0x40, 0x1f, 0x3f, 0x32, 0x4a, 0x4f,
|
||||
0x1f, 0x2a, 0x29, 0xf0, 0x23, 0x99, 0xbe, 0xc7,
|
||||
0x49, 0x7e, 0xc7, 0x60, 0xa3, 0x54, 0x9c, 0x06,
|
||||
0x06, 0xb4, 0xc4, 0x4d, 0x07, 0xd5, 0xef, 0xa0,
|
||||
0xcd, 0xac, 0x8f, 0x54, 0xf9, 0x15, 0x15, 0xa0,
|
||||
0xb5, 0xf1, 0x7d, 0xde, 0xeb, 0xe0, 0x41, 0x9a,
|
||||
0x3e, 0x5a, 0x66, 0xd2, 0xfc, 0x08, 0x00, 0xe8,
|
||||
0xf2, 0xc9, 0x04, 0x94, 0x7e, 0x0b, 0x06, 0x56,
|
||||
0x9d, 0xe7, 0x1f, 0x48, 0x21, 0x4d, 0x18, 0x21,
|
||||
0xa3, 0x5f, 0x69, 0x7a, 0xed, 0x4d, 0x2d, 0xb4,
|
||||
0x3e, 0x34, 0xcf, 0x9e, 0xae, 0x11, 0x2d, 0x15,
|
||||
0xbc, 0x2c, 0xd7, 0xef, 0x1c, 0xd5, 0xd8, 0xd3,
|
||||
0xc2, 0x4f, 0xcb, 0x2c, 0xb1, 0xa3, 0x85, 0x0a,
|
||||
0xad, 0x44, 0x68, 0x42, 0xab, 0x40, 0x58, 0xa2,
|
||||
0x9b, 0xdf, 0xbd, 0xa2, 0x10, 0xc7, 0x2e, 0x5a,
|
||||
0x1d, 0x07, 0xe4, 0xb5, 0xa6, 0xa8, 0xb3, 0x12,
|
||||
0x44, 0x64, 0x18, 0xaf, 0xa7, 0x72, 0x05, 0x29,
|
||||
0x48, 0xc6, 0x78, 0xef, 0x7f, 0x54, 0xe9, 0xb3,
|
||||
0xae, 0x10, 0x51, 0x11, 0x91, 0xf6, 0x96, 0x40,
|
||||
0x6a, 0xf1, 0xc8, 0x3d, 0x46, 0x86, 0x2e, 0xd3,
|
||||
0xa1, 0xdc, 0x38, 0x97, 0x11, 0x49, 0x4b, 0x03,
|
||||
0x91, 0xa5, 0xed, 0x53, 0x3f, 0xd5, 0x87, 0x04,
|
||||
0x25, 0xf0, 0xe2, 0x19, 0x55, 0x99, 0x6b, 0xad,
|
||||
0xa2, 0x9c, 0x7a, 0xcd, 0xc9, 0x34, 0x18, 0x8f,
|
||||
0x4c, 0xaa, 0xed, 0xd3, 0x49, 0x98, 0xd9, 0x67,
|
||||
0xbe, 0x41, 0x85, 0xbf, 0x09, 0x22, 0x16, 0xa0,
|
||||
0x81, 0x71, 0x38, 0xdb, 0xb2, 0xd6, 0xfe, 0x2b,
|
||||
0xf4, 0x03, 0xf8, 0xd1, 0x00, 0x16, 0x5e, 0x77,
|
||||
0x69, 0xd4, 0x28, 0x8f, 0x94, 0x4d, 0x58, 0x4f,
|
||||
0xae, 0x6b, 0xb1, 0x09, 0x85, 0x71, 0xd7, 0x3a,
|
||||
0x4b, 0xea, 0xd2, 0x70, 0xbb, 0xa2, 0x20, 0x85,
|
||||
0x2d, 0xbd, 0x57, 0x3f, 0x81, 0x3e, 0xe4, 0xa2,
|
||||
0x43, 0x3e, 0xee, 0x04, 0xbe, 0x42, 0x51, 0xaa,
|
||||
0xad, 0xda, 0x53, 0x87, 0xeb, 0xc0, 0x9a, 0xde,
|
||||
0xa2, 0x7e, 0x19, 0x4f, 0xa7, 0xdf, 0x23, 0x96,
|
||||
0xdd, 0xe3, 0x18, 0x0f, 0xc0, 0x48, 0xfd, 0x9f,
|
||||
0x13, 0xc3, 0x3c, 0x3a, 0x7b, 0x7c, 0xa8, 0x89,
|
||||
0x6a, 0xd6, 0x68, 0xdd, 0x3b, 0xc6, 0xc6, 0x44,
|
||||
0x79, 0xc5, 0x59, 0x1c, 0x23, 0xa9, 0x8b, 0x00,
|
||||
0x62, 0xeb, 0x3a, 0x51, 0x14, 0x5e, 0x63, 0x79,
|
||||
0x97, 0x60, 0x62, 0x13, 0x49, 0x41, 0x06, 0xf4,
|
||||
0x5f, 0xe7, 0x9b, 0xd6, 0x51, 0x31, 0x6e, 0x7d,
|
||||
0x5e, 0xe8, 0x72, 0xce, 0x5e, 0xd0, 0xa7, 0x9d,
|
||||
0xa3, 0xa9, 0x92, 0xa8, 0x2f, 0x78, 0x00, 0x92,
|
||||
0x4f, 0xf0, 0x21, 0xb6, 0x74, 0xfb, 0x3c, 0xe2,
|
||||
0x60, 0xdf, 0x82, 0x09, 0x07, 0xb3, 0x68, 0x5b,
|
||||
0xe2, 0x49, 0xeb, 0x81, 0x98, 0x23, 0x19, 0xdc,
|
||||
0x2c, 0xa1, 0xad, 0x95, 0xc0, 0x9a, 0x48, 0xcd,
|
||||
0xa8, 0xe4, 0xdf, 0xbb, 0xca, 0xd4, 0x47, 0x46,
|
||||
0xe2, 0xdb, 0x5c, 0xe2, 0xdb, 0xb2, 0x32, 0x44,
|
||||
0x87, 0x0a, 0x49, 0x99, 0xc6, 0x32, 0x75, 0xc6,
|
||||
0xe7, 0x1b, 0xf1, 0x71, 0x74, 0x32, 0x4a, 0x8d,
|
||||
0xf2, 0x0a, 0x64, 0xff, 0xe4, 0x0d, 0xf4, 0xa2,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
lzmaTest{
|
||||
"hello world test with corrupt lc, lp, pb in header",
|
||||
3,
|
||||
true,
|
||||
"hello world\n",
|
||||
[]byte{
|
||||
0xfe, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x19,
|
||||
0x49, 0xee, 0x8d, 0xe9, 0x17, 0x89, 0x3a, 0x33,
|
||||
0x5f, 0xfc, 0xac, 0xf7, 0x20, 0x00,
|
||||
},
|
||||
headerError,
|
||||
},
|
||||
}
|
||||
|
||||
type lzmaBenchmark struct {
|
||||
descr string
|
||||
level int
|
||||
raw []byte
|
||||
lzma []byte
|
||||
}
|
||||
|
||||
func readFile(filename string) []byte {
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
var bench = lzmaBenchmark{
|
||||
descr: "text bench with size == -1",
|
||||
level: 3,
|
||||
raw: readFile("data/data.txt"),
|
||||
lzma: readFile("data/data.eos.l3.lzma"),
|
||||
}
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The lzma package implements reading and writing of LZMA format compressed data.
|
||||
// Reference implementation is LZMA SDK version 4.65 originaly developed by Igor
|
||||
// Pavlov, available online at:
|
||||
//
|
||||
// http://www.7-zip.org/sdk.html
|
||||
//
|
||||
//
|
||||
//
|
||||
// Usage examples. Write compressed data to a buffer:
|
||||
//
|
||||
// var b bytes.Buffer
|
||||
// w := lzma.NewWriter(&b)
|
||||
// w.Write([]byte("hello, world\n"))
|
||||
// w.Close()
|
||||
//
|
||||
// read that data back:
|
||||
//
|
||||
// r := lzma.NewReader(&b)
|
||||
// io.Copy(os.Stdout, r)
|
||||
// r.Close()
|
||||
//
|
||||
//
|
||||
//
|
||||
// If the data is bigger than you'd like to hold into memory, use pipes. Write
|
||||
// compressed data to an io.PipeWriter:
|
||||
//
|
||||
// pr, pw := io.Pipe()
|
||||
// go func() {
|
||||
// defer pw.Close()
|
||||
// w := lzma.NewWriter(pw)
|
||||
// defer w.Close()
|
||||
// // the bytes.Buffer would be an io.Reader used to read uncompressed data from
|
||||
// io.Copy(w, bytes.NewBuffer([]byte("hello, world\n")))
|
||||
// }()
|
||||
//
|
||||
// and read it back:
|
||||
//
|
||||
// defer pr.Close()
|
||||
// r := lzma.NewReader(pr)
|
||||
// defer r.Close()
|
||||
// // the os.Stdout would be an io.Writer used to write uncompressed data to
|
||||
// io.Copy(os.Stdout, r)
|
||||
//
|
||||
//
|
||||
//
|
||||
package lzma
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
inBufSize = 1 << 16
|
||||
outBufSize = 1 << 16
|
||||
lzmaPropSize = 5
|
||||
lzmaHeaderSize = lzmaPropSize + 8
|
||||
lzmaMaxReqInputSize = 20
|
||||
|
||||
kNumRepDistances = 4
|
||||
kNumStates = 12
|
||||
kNumPosSlotBits = 6
|
||||
kDicLogSizeMin = 0
|
||||
kNumLenToPosStatesBits = 2
|
||||
kNumLenToPosStates = 1 << kNumLenToPosStatesBits
|
||||
kMatchMinLen = 2
|
||||
kNumAlignBits = 4
|
||||
kAlignTableSize = 1 << kNumAlignBits
|
||||
kAlignMask = kAlignTableSize - 1
|
||||
kStartPosModelIndex = 4
|
||||
kEndPosModelIndex = 14
|
||||
kNumPosModels = kEndPosModelIndex - kStartPosModelIndex
|
||||
kNumFullDistances = 1 << (kEndPosModelIndex / 2)
|
||||
kNumLitPosStatesBitsEncodingMax = 4
|
||||
kNumLitContextBitsMax = 8
|
||||
kNumPosStatesBitsMax = 4
|
||||
kNumPosStatesMax = 1 << kNumPosStatesBitsMax
|
||||
kNumLowLenBits = 3
|
||||
kNumMidLenBits = 3
|
||||
kNumHighLenBits = 8
|
||||
kNumLowLenSymbols = 1 << kNumLowLenBits
|
||||
kNumMidLenSymbols = 1 << kNumMidLenBits
|
||||
kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + (1 << kNumHighLenBits)
|
||||
kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1
|
||||
)
|
||||
|
||||
// A streamError reports the presence of corrupt input stream.
|
||||
var streamError = errors.New("error in lzma encoded data stream")
|
||||
|
||||
// A headerError reports an error in the header of the lzma encoder file.
|
||||
var headerError = errors.New("error in lzma header")
|
||||
|
||||
// A nReadError reports what its message reads
|
||||
var nReadError = errors.New("number of bytes returned by Reader.Read() didn't meet expectances")
|
||||
|
||||
// A nWriteError reports what its message reads
|
||||
var nWriteError = errors.New("number of bytes returned by Writer.Write() didn't meet expectances")
|
||||
|
||||
// TODO: implement this err
|
||||
// A dataIntegrityError reports an error encountered while cheching data integrity.
|
||||
// -- from lzma.txt:
|
||||
// You can use multiple checks to test data integrity after full decompression:
|
||||
// 1) Check Result and "status" variable.
|
||||
// 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
|
||||
// 3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
|
||||
// You must use correct finish mode in that case.
|
||||
//
|
||||
//type dataIntegrityError struct {
|
||||
// msg string
|
||||
// // hz
|
||||
//}
|
||||
|
||||
func stateUpdateChar(index uint32) uint32 {
|
||||
if index < 4 {
|
||||
return 0
|
||||
}
|
||||
if index < 10 {
|
||||
return index - 3
|
||||
}
|
||||
return index - 6
|
||||
}
|
||||
|
||||
func stateUpdateMatch(index uint32) uint32 {
|
||||
if index < 7 {
|
||||
return 7
|
||||
}
|
||||
return 10
|
||||
}
|
||||
|
||||
func stateUpdateRep(index uint32) uint32 {
|
||||
if index < 7 {
|
||||
return 8
|
||||
}
|
||||
return 11
|
||||
}
|
||||
|
||||
func stateUpdateShortRep(index uint32) uint32 {
|
||||
if index < 7 {
|
||||
return 9
|
||||
}
|
||||
return 11
|
||||
}
|
||||
|
||||
func stateIsCharState(index uint32) bool {
|
||||
if index < 7 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getLenToPosState(length uint32) uint32 {
|
||||
length -= kMatchMinLen
|
||||
if length < kNumLenToPosStates {
|
||||
return length
|
||||
}
|
||||
return kNumLenToPosStates - 1
|
||||
}
|
||||
|
||||
// LZMA compressed file format
|
||||
// ---------------------------
|
||||
// Offset Size Description
|
||||
// 0 1 Special LZMA properties (lc,lp, pb in encoded form)
|
||||
// 1 4 Dictionary size (little endian)
|
||||
// 5 8 Uncompressed size (little endian). Size -1 stands for unknown size
|
||||
|
||||
// lzma properties
|
||||
type props struct {
|
||||
litContextBits, // lc
|
||||
litPosStateBits, // lp
|
||||
posStateBits uint8 // pb
|
||||
dictSize uint32
|
||||
}
|
||||
|
||||
func (p *props) decodeProps(buf []byte) {
|
||||
d := buf[0]
|
||||
if d > (9 * 5 * 5) {
|
||||
throw(headerError)
|
||||
}
|
||||
p.litContextBits = d % 9
|
||||
d /= 9
|
||||
p.posStateBits = d / 5
|
||||
p.litPosStateBits = d % 5
|
||||
if p.litContextBits > kNumLitContextBitsMax || p.litPosStateBits > 4 || p.posStateBits > kNumPosStatesBitsMax {
|
||||
throw(headerError)
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
p.dictSize += uint32(buf[i+1]) << uint32(i*8)
|
||||
}
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
// i/o
|
||||
rd *rangeDecoder // r
|
||||
outWin *lzOutWindow // w
|
||||
|
||||
// lzma header
|
||||
prop *props
|
||||
unpackSize int64
|
||||
|
||||
// hz
|
||||
matchDecoders []uint16
|
||||
repDecoders []uint16
|
||||
repG0Decoders []uint16
|
||||
repG1Decoders []uint16
|
||||
repG2Decoders []uint16
|
||||
rep0LongDecoders []uint16
|
||||
posSlotCoders []*rangeBitTreeCoder
|
||||
posDecoders []uint16
|
||||
posAlignCoder *rangeBitTreeCoder
|
||||
lenCoder *lenCoder
|
||||
repLenCoder *lenCoder
|
||||
litCoder *litCoder
|
||||
dictSizeCheck uint32
|
||||
posStateMask uint32
|
||||
}
|
||||
|
||||
func (z *decoder) doDecode() {
|
||||
var state uint32 = 0
|
||||
var rep0 uint32 = 0
|
||||
var rep1 uint32 = 0
|
||||
var rep2 uint32 = 0
|
||||
var rep3 uint32 = 0
|
||||
var nowPos uint64 = 0
|
||||
var prevByte byte = 0
|
||||
|
||||
for z.unpackSize < 0 || int64(nowPos) < z.unpackSize {
|
||||
posState := uint32(nowPos) & z.posStateMask
|
||||
if z.rd.decodeBit(z.matchDecoders, state<<kNumPosStatesBitsMax+posState) == 0 {
|
||||
lsc := z.litCoder.getSubCoder(uint32(nowPos), prevByte)
|
||||
if !stateIsCharState(state) {
|
||||
prevByte = lsc.decodeWithMatchByte(z.rd, z.outWin.getByte(rep0))
|
||||
} else {
|
||||
prevByte = lsc.decodeNormal(z.rd)
|
||||
}
|
||||
z.outWin.putByte(prevByte)
|
||||
state = stateUpdateChar(state)
|
||||
nowPos++
|
||||
} else {
|
||||
var length uint32
|
||||
if z.rd.decodeBit(z.repDecoders, state) == 1 {
|
||||
length = 0
|
||||
if z.rd.decodeBit(z.repG0Decoders, state) == 0 {
|
||||
if z.rd.decodeBit(z.rep0LongDecoders, state<<kNumPosStatesBitsMax+posState) == 0 {
|
||||
state = stateUpdateShortRep(state)
|
||||
length = 1
|
||||
}
|
||||
} else {
|
||||
var distance uint32
|
||||
if z.rd.decodeBit(z.repG1Decoders, state) == 0 {
|
||||
distance = rep1
|
||||
} else {
|
||||
if z.rd.decodeBit(z.repG2Decoders, state) == 0 {
|
||||
distance = rep2
|
||||
} else {
|
||||
distance, rep3 = rep3, rep2
|
||||
}
|
||||
rep2 = rep1
|
||||
}
|
||||
rep1, rep0 = rep0, distance
|
||||
}
|
||||
if length == 0 {
|
||||
length = z.repLenCoder.decode(z.rd, posState) + kMatchMinLen
|
||||
state = stateUpdateRep(state)
|
||||
}
|
||||
} else {
|
||||
rep3, rep2, rep1 = rep2, rep1, rep0
|
||||
length = z.lenCoder.decode(z.rd, posState) + kMatchMinLen
|
||||
state = stateUpdateMatch(state)
|
||||
posSlot := z.posSlotCoders[getLenToPosState(length)].decode(z.rd)
|
||||
if posSlot >= kStartPosModelIndex {
|
||||
numDirectBits := posSlot>>1 - 1
|
||||
rep0 = (2 | posSlot&1) << numDirectBits
|
||||
if posSlot < kEndPosModelIndex {
|
||||
rep0 += reverseDecodeIndex(z.rd, z.posDecoders, rep0-posSlot-1, numDirectBits)
|
||||
} else {
|
||||
rep0 += z.rd.decodeDirectBits(numDirectBits-kNumAlignBits) << kNumAlignBits
|
||||
rep0 += z.posAlignCoder.reverseDecode(z.rd)
|
||||
if int32(rep0) < 0 {
|
||||
if rep0 == 0xFFFFFFFF {
|
||||
break
|
||||
}
|
||||
throw(streamError)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rep0 = posSlot
|
||||
}
|
||||
}
|
||||
if uint64(rep0) >= nowPos || rep0 >= z.dictSizeCheck {
|
||||
throw(streamError)
|
||||
}
|
||||
z.outWin.copyBlock(rep0, length)
|
||||
nowPos += uint64(length)
|
||||
prevByte = z.outWin.getByte(0)
|
||||
}
|
||||
}
|
||||
z.outWin.flush()
|
||||
//if z.unpackSize != -1 {
|
||||
// if z.outWin.unpacked != z.unpackSize {
|
||||
// throw(&dataIntegrityError{})
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
func (z *decoder) decoder(r io.Reader, w io.Writer) (err error) {
|
||||
defer handlePanics(&err)
|
||||
|
||||
// read 13 bytes (lzma header)
|
||||
header := make([]byte, lzmaHeaderSize)
|
||||
n, err := r.Read(header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != lzmaHeaderSize {
|
||||
return nReadError
|
||||
}
|
||||
z.prop = &props{}
|
||||
z.prop.decodeProps(header)
|
||||
|
||||
z.unpackSize = 0
|
||||
for i := 0; i < 8; i++ {
|
||||
b := header[lzmaPropSize+i]
|
||||
z.unpackSize = z.unpackSize | int64(b)<<uint64(8*i)
|
||||
}
|
||||
|
||||
// do not move before r.Read(header)
|
||||
z.rd = newRangeDecoder(r)
|
||||
|
||||
z.dictSizeCheck = maxUInt32(z.prop.dictSize, 1)
|
||||
z.outWin = newLzOutWindow(w, maxUInt32(z.dictSizeCheck, 1<<12))
|
||||
|
||||
z.litCoder = newLitCoder(uint32(z.prop.litPosStateBits), uint32(z.prop.litContextBits))
|
||||
z.lenCoder = newLenCoder(uint32(1 << z.prop.posStateBits))
|
||||
z.repLenCoder = newLenCoder(uint32(1 << z.prop.posStateBits))
|
||||
z.posStateMask = uint32(1<<z.prop.posStateBits - 1)
|
||||
z.matchDecoders = initBitModels(kNumStates << kNumPosStatesBitsMax)
|
||||
z.repDecoders = initBitModels(kNumStates)
|
||||
z.repG0Decoders = initBitModels(kNumStates)
|
||||
z.repG1Decoders = initBitModels(kNumStates)
|
||||
z.repG2Decoders = initBitModels(kNumStates)
|
||||
z.rep0LongDecoders = initBitModels(kNumStates << kNumPosStatesBitsMax)
|
||||
z.posDecoders = initBitModels(kNumFullDistances - kEndPosModelIndex)
|
||||
z.posSlotCoders = make([]*rangeBitTreeCoder, kNumLenToPosStates)
|
||||
for i := 0; i < kNumLenToPosStates; i++ {
|
||||
z.posSlotCoders[i] = newRangeBitTreeCoder(kNumPosSlotBits)
|
||||
}
|
||||
z.posAlignCoder = newRangeBitTreeCoder(kNumAlignBits)
|
||||
|
||||
z.doDecode()
|
||||
return
|
||||
}
|
||||
|
||||
// NewReader returns a new ReadCloser that can be used to read the uncompressed
|
||||
// version of r. It is the caller's responsibility to call Close on the ReadCloser
|
||||
// when finished reading.
|
||||
//
|
||||
func NewReader(r io.Reader) io.ReadCloser {
|
||||
var z decoder
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
err := z.decoder(r, pw)
|
||||
pw.CloseWithError(err)
|
||||
}()
|
||||
return pr
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecoder(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
for _, tt := range lzmaTests {
|
||||
in := bytes.NewBuffer(tt.lzma)
|
||||
r := NewReader(in)
|
||||
defer r.Close()
|
||||
b.Reset()
|
||||
n, err := io.Copy(b, r)
|
||||
if err != tt.err {
|
||||
t.Errorf("%s: io.Copy: %v, want %v", tt.descr, err, tt.err)
|
||||
}
|
||||
if err == nil { // if err != nil, there is little chance that data is decoded correctly, if at all
|
||||
s := b.String()
|
||||
if s != tt.raw {
|
||||
t.Errorf("%s: got %d-byte %q, want %d-byte %q", tt.descr, n, s, len(tt.raw), tt.raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecoder(b *testing.B) {
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
in := bytes.NewBuffer(bench.lzma)
|
||||
b.StartTimer()
|
||||
// timer starts before this contructor because variable "in" already
|
||||
// contains data, so the decoding start rigth away
|
||||
r := NewReader(in)
|
||||
n, err := io.Copy(buf, r)
|
||||
b.StopTimer()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
b.SetBytes(n)
|
||||
r.Close()
|
||||
}
|
||||
if bytes.Equal(buf.Bytes(), bench.raw) == false { // check only after last iteration
|
||||
log.Fatalf("%s: got %d-byte %q, want %d-byte %q", bench.descr, len(buf.Bytes()), buf.String(), len(bench.raw), bench.raw)
|
||||
}
|
||||
}
|
||||
+1150
File diff suppressed because it is too large
Load Diff
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func pipe(t *testing.T, efunc func(io.WriteCloser), dfunc func(io.ReadCloser), size int64) {
|
||||
level := 3
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
ze := NewWriterSizeLevel(pw, size, level)
|
||||
defer ze.Close()
|
||||
efunc(ze)
|
||||
}()
|
||||
defer pr.Close()
|
||||
zd := NewReader(pr)
|
||||
defer zd.Close()
|
||||
dfunc(zd)
|
||||
}
|
||||
|
||||
func testEmpty(t *testing.T, sizeIsKnown bool) {
|
||||
size := int64(-1)
|
||||
if sizeIsKnown == true {
|
||||
size = 0
|
||||
}
|
||||
pipe(t,
|
||||
func(w io.WriteCloser) {},
|
||||
func(r io.ReadCloser) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if len(b) != 0 {
|
||||
t.Fatalf("did not read an empty slice")
|
||||
}
|
||||
},
|
||||
size)
|
||||
}
|
||||
|
||||
func TestEmpty1(t *testing.T) {
|
||||
testEmpty(t, true)
|
||||
}
|
||||
|
||||
func TestEmpty2(t *testing.T) {
|
||||
testEmpty(t, false)
|
||||
}
|
||||
|
||||
func testBoth(t *testing.T, sizeIsKnown bool) {
|
||||
size := int64(-1)
|
||||
payload := []byte("lzmalzmalzma")
|
||||
if sizeIsKnown == true {
|
||||
size = int64(len(payload))
|
||||
}
|
||||
pipe(t,
|
||||
func(w io.WriteCloser) {
|
||||
n, err := w.Write(payload)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if n != len(payload) {
|
||||
t.Fatalf("wrote %d bytes, want %d bytes", n, len(payload))
|
||||
}
|
||||
},
|
||||
func(r io.ReadCloser) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if string(b) != string(payload) {
|
||||
t.Fatalf("payload is %s, want %s", string(b), string(payload))
|
||||
}
|
||||
},
|
||||
size)
|
||||
}
|
||||
|
||||
func TestBoth1(t *testing.T) {
|
||||
testBoth(t, true)
|
||||
}
|
||||
|
||||
func TestBoth2(t *testing.T) {
|
||||
testBoth(t, false)
|
||||
}
|
||||
|
||||
func TestEncoder(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
for _, tt := range lzmaTests {
|
||||
if tt.err == nil {
|
||||
pr, pw := io.Pipe()
|
||||
defer pr.Close()
|
||||
in := bytes.NewBuffer([]byte(tt.raw))
|
||||
size := int64(-1)
|
||||
if tt.size == true {
|
||||
size = int64(len([]byte(tt.raw)))
|
||||
}
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
w := NewWriterSizeLevel(pw, size, tt.level)
|
||||
defer w.Close()
|
||||
_, err := io.Copy(w, in)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}()
|
||||
b.Reset()
|
||||
_, err := io.Copy(b, pr)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
res := b.Bytes()
|
||||
if bytes.Equal(res, tt.lzma) == false {
|
||||
t.Errorf("%s: got %d-byte %q, want %d-byte %q", tt.descr, len(res), string(res), len(tt.lzma), string(tt.lzma))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncoder(b *testing.B) {
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
start := make(chan bool)
|
||||
for i := 0; i < b.N; i++ {
|
||||
in := bytes.NewBuffer(bench.raw)
|
||||
pr, pw := io.Pipe()
|
||||
defer pr.Close()
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
w := NewWriterLevel(pw, bench.level)
|
||||
defer w.Close()
|
||||
start <- true
|
||||
n, err := io.Copy(w, in)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
b.SetBytes(n)
|
||||
}()
|
||||
buf.Reset()
|
||||
<-start // wait for encoder to start encoding before starting the timer
|
||||
b.StartTimer()
|
||||
_, err := io.Copy(buf, pr)
|
||||
b.StopTimer()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
if bytes.Equal(buf.Bytes(), bench.lzma) == false { // check only once, not at every iteration
|
||||
log.Fatalf("%s: got %d-byte %q, want %d-byte %q", bench.descr, len(buf.Bytes()), buf.String(), len(bench.lzma), string(bench.lzma))
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of the author nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
//"compress/lzma"
|
||||
"github.com/kjk/lzma"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
stdout = flag.Bool("c", false, "write on standard output, keep original files unchanged")
|
||||
decompress = flag.Bool("d", false, "decompress; see also -c and -k")
|
||||
force = flag.Bool("f", false, "force overwrite of output file")
|
||||
help = flag.Bool("h", false, "print this help message")
|
||||
keep = flag.Bool("k", false, "keep original files unchaned")
|
||||
suffix = flag.String("s", "lzma", "use provided suffix on compressed files")
|
||||
level = flag.Int("l", 5, "compression level [1 ... 9]")
|
||||
cores = flag.Int("cores", 1, "number of cores to use for parallelization")
|
||||
|
||||
stdin bool
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [OPTION]... [FILE]\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "Compress or uncompress FILE (by default, compress FILE in-place).\n\n")
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintf(os.Stderr, "\nWith no FILE, or when FILE is -, read standard input.\n")
|
||||
}
|
||||
|
||||
func exit(msg string) {
|
||||
usage()
|
||||
fmt.Fprintln(os.Stderr)
|
||||
log.Fatalf("%s: check args: %s\n\n", os.Args[0], msg)
|
||||
}
|
||||
|
||||
func setByUser(name string) (isSet bool) {
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
if f.Name == name {
|
||||
isSet = true
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *help == true {
|
||||
usage()
|
||||
log.Fatal(0)
|
||||
}
|
||||
//if *stdout == true && *suffix != "lzma" {
|
||||
if *stdout == true && setByUser("s") == true {
|
||||
exit("stdout set, suffix not used")
|
||||
}
|
||||
if *stdout == true && *force == true {
|
||||
exit("stdout set, force not used")
|
||||
}
|
||||
if *stdout == true && *keep == true {
|
||||
exit("stdout set, keep is redundant")
|
||||
}
|
||||
if flag.NArg() > 1 {
|
||||
exit("too many file, provide at most one file at a time or check order of flags")
|
||||
}
|
||||
//if *decompress == true && *level != 5 {
|
||||
if *decompress == true && setByUser("l") == true {
|
||||
exit("compression level is used for compression only")
|
||||
}
|
||||
if *level < 1 || *level > 9 {
|
||||
exit("compression level out of range")
|
||||
}
|
||||
if *cores < 1 || *cores > 32 {
|
||||
exit("invalid number of cores")
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(*cores)
|
||||
|
||||
var inFilePath string
|
||||
var outFilePath string
|
||||
if flag.NArg() == 0 || flag.NArg() == 1 && flag.Args()[0] == "-" { // parse args: read from stdin
|
||||
if *stdout != true {
|
||||
exit("reading from stdin, can write only to stdout")
|
||||
}
|
||||
//if *suffix != "lzma" {
|
||||
if setByUser("s") == true {
|
||||
exit("reading from stdin, suffix not needed")
|
||||
}
|
||||
stdin = true
|
||||
|
||||
} else if flag.NArg() == 1 { // parse args: read from file
|
||||
inFilePath = flag.Args()[0]
|
||||
f, err := os.Lstat(inFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if f == nil {
|
||||
exit(fmt.Sprintf("file %s not found", inFilePath))
|
||||
}
|
||||
if !!f.IsDir() {
|
||||
exit(fmt.Sprintf("%s is not a regular file", inFilePath))
|
||||
}
|
||||
|
||||
if *stdout == false { // parse args: write to file
|
||||
if *suffix == "" {
|
||||
exit("suffix can't be an empty string")
|
||||
}
|
||||
|
||||
if *decompress == true {
|
||||
outFileDir, outFileName := path.Split(inFilePath)
|
||||
if strings.HasSuffix(outFileName, "."+*suffix) {
|
||||
if len(outFileName) > len("."+*suffix) {
|
||||
nstr := strings.SplitN(outFileName, ".", len(outFileName))
|
||||
estr := strings.Join(nstr[0:len(nstr)-1], ".")
|
||||
outFilePath = outFileDir + estr
|
||||
} else {
|
||||
log.Fatalf("error: can't strip suffix .%s from file %s", *suffix, inFilePath)
|
||||
}
|
||||
} else {
|
||||
exit(fmt.Sprintf("file %s doesn't have suffix .%s", inFilePath, *suffix))
|
||||
}
|
||||
|
||||
} else {
|
||||
outFilePath = inFilePath + "." + *suffix
|
||||
}
|
||||
|
||||
f, err = os.Lstat(outFilePath)
|
||||
if err != nil && f != nil { // should be: ||| if err != nil && err != "file not found" ||| but i can't find the error's id
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if f != nil && !f.IsDir() {
|
||||
if *force == true {
|
||||
err = os.Remove(outFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
} else {
|
||||
exit(fmt.Sprintf("outFile %s exists. use force to overwrite", outFilePath))
|
||||
}
|
||||
} else if f != nil {
|
||||
exit(fmt.Sprintf("outFile %s exists and is not a regular file", outFilePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
//defer pr.Close()
|
||||
//defer pw.Close()
|
||||
|
||||
if *decompress {
|
||||
// read from inFile into pw
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
var inFile *os.File
|
||||
var err error
|
||||
if stdin == true {
|
||||
inFile = os.Stdin
|
||||
} else {
|
||||
inFile, err = os.Open(inFilePath)
|
||||
}
|
||||
defer inFile.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
_, err = io.Copy(pw, inFile)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// write into outFile from z
|
||||
defer pr.Close()
|
||||
z := lzma.NewReader(pr)
|
||||
defer z.Close()
|
||||
var outFile *os.File
|
||||
var err error
|
||||
if *stdout == true {
|
||||
outFile = os.Stdout
|
||||
} else {
|
||||
outFile, err = os.Create(outFilePath)
|
||||
}
|
||||
defer outFile.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, z)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
} else {
|
||||
// read from inFile into z
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
var z io.WriteCloser
|
||||
var inFile *os.File
|
||||
var err error
|
||||
if stdin == true {
|
||||
inFile = os.Stdin
|
||||
defer inFile.Close()
|
||||
z = lzma.NewWriterLevel(pw, *level)
|
||||
defer z.Close()
|
||||
} else {
|
||||
inFile, err = os.Open(inFilePath)
|
||||
defer inFile.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
f, err := os.Lstat(inFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
z = lzma.NewWriterSizeLevel(pw, int64(f.Size()), *level)
|
||||
defer z.Close()
|
||||
}
|
||||
|
||||
_, err = io.Copy(z, inFile)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
// write into outFile from pr
|
||||
defer pr.Close()
|
||||
var outFile *os.File
|
||||
var err error
|
||||
if *stdout == true {
|
||||
outFile = os.Stdout
|
||||
} else {
|
||||
outFile, err = os.Create(outFilePath)
|
||||
}
|
||||
defer outFile.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, pr)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if *stdout == false && *keep == false {
|
||||
err := os.Remove(inFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
type lenCoder struct {
|
||||
choice []uint16
|
||||
lowCoder []*rangeBitTreeCoder
|
||||
midCoder []*rangeBitTreeCoder
|
||||
highCoder *rangeBitTreeCoder
|
||||
}
|
||||
|
||||
func newLenCoder(numPosStates /*1 << pb*/ uint32) *lenCoder {
|
||||
lc := &lenCoder{
|
||||
choice: initBitModels(2),
|
||||
lowCoder: make([]*rangeBitTreeCoder, kNumPosStatesMax),
|
||||
midCoder: make([]*rangeBitTreeCoder, kNumPosStatesMax),
|
||||
highCoder: newRangeBitTreeCoder(kNumHighLenBits),
|
||||
}
|
||||
for i := uint32(0); i < numPosStates; i++ {
|
||||
lc.lowCoder[i] = newRangeBitTreeCoder(kNumLowLenBits)
|
||||
lc.midCoder[i] = newRangeBitTreeCoder(kNumMidLenBits)
|
||||
}
|
||||
return lc
|
||||
}
|
||||
|
||||
func (lc *lenCoder) decode(rd *rangeDecoder, posState uint32) (res uint32) {
|
||||
i := rd.decodeBit(lc.choice, 0)
|
||||
if i == 0 {
|
||||
res = lc.lowCoder[posState].decode(rd)
|
||||
return
|
||||
}
|
||||
res = kNumLowLenSymbols
|
||||
j := rd.decodeBit(lc.choice, 1)
|
||||
if j == 0 {
|
||||
k := lc.midCoder[posState].decode(rd)
|
||||
res += k
|
||||
return
|
||||
} else {
|
||||
l := lc.highCoder.decode(rd)
|
||||
res = res + kNumMidLenSymbols + l
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (lc *lenCoder) encode(re *rangeEncoder, symbol, posState uint32) {
|
||||
if symbol < kNumLowLenSymbols {
|
||||
re.encode(lc.choice, 0, 0)
|
||||
lc.lowCoder[posState].encode(re, symbol)
|
||||
} else {
|
||||
symbol -= kNumLowLenSymbols
|
||||
re.encode(lc.choice, 0, 1)
|
||||
if symbol < kNumMidLenSymbols {
|
||||
re.encode(lc.choice, 1, 0)
|
||||
lc.midCoder[posState].encode(re, symbol)
|
||||
} else {
|
||||
re.encode(lc.choice, 1, 1)
|
||||
lc.highCoder.encode(re, symbol-kNumMidLenSymbols)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write prices into prices []uint32
|
||||
func (lc *lenCoder) setPrices(prices []uint32, posState, numSymbols, st uint32) {
|
||||
a0 := getPrice0(lc.choice[0])
|
||||
a1 := getPrice1(lc.choice[0])
|
||||
b0 := a1 + getPrice0(lc.choice[1])
|
||||
b1 := a1 + getPrice1(lc.choice[1])
|
||||
|
||||
var i uint32
|
||||
for i = 0; i < kNumLowLenSymbols; i++ {
|
||||
if i >= numSymbols {
|
||||
return
|
||||
}
|
||||
prices[st+i] = a0 + lc.lowCoder[posState].getPrice(i)
|
||||
}
|
||||
for ; i < kNumLowLenSymbols+kNumMidLenSymbols; i++ {
|
||||
if i >= numSymbols {
|
||||
return
|
||||
}
|
||||
prices[st+i] = b0 + lc.midCoder[posState].getPrice(i-kNumLowLenSymbols)
|
||||
}
|
||||
for ; i < numSymbols; i++ {
|
||||
prices[st+i] = b1 + lc.highCoder.getPrice(i-kNumLowLenSymbols-kNumMidLenSymbols)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type lenPriceTableCoder struct {
|
||||
lc *lenCoder
|
||||
prices []uint32
|
||||
counters []uint32
|
||||
tableSize uint32
|
||||
}
|
||||
|
||||
func newLenPriceTableCoder(tableSize, numPosStates uint32) *lenPriceTableCoder {
|
||||
pc := &lenPriceTableCoder{
|
||||
lc: newLenCoder(numPosStates),
|
||||
prices: make([]uint32, kNumLenSymbols<<kNumPosStatesBitsMax),
|
||||
counters: make([]uint32, kNumPosStatesMax),
|
||||
tableSize: tableSize,
|
||||
}
|
||||
for posState := uint32(0); posState < numPosStates; posState++ {
|
||||
pc.updateTable(posState)
|
||||
}
|
||||
return pc
|
||||
}
|
||||
|
||||
func (pc *lenPriceTableCoder) updateTable(posState uint32) {
|
||||
pc.lc.setPrices(pc.prices, posState, pc.tableSize, posState*kNumLenSymbols)
|
||||
pc.counters[posState] = pc.tableSize
|
||||
}
|
||||
|
||||
func (pc *lenPriceTableCoder) getPrice(symbol, posState uint32) uint32 {
|
||||
return pc.prices[posState*kNumLenSymbols+symbol]
|
||||
}
|
||||
|
||||
func (pc *lenPriceTableCoder) encode(re *rangeEncoder, symbol, posState uint32) {
|
||||
pc.lc.encode(re, symbol, posState)
|
||||
pc.counters[posState]--
|
||||
if pc.counters[posState] == 0 {
|
||||
pc.updateTable(posState)
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
type litSubCoder struct {
|
||||
coders []uint16
|
||||
}
|
||||
|
||||
func newLitSubCoder() *litSubCoder {
|
||||
return &litSubCoder{
|
||||
coders: initBitModels(0x300),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (lsc *litSubCoder) decodeNormal(rd *rangeDecoder) byte {
|
||||
symbol := uint32(1)
|
||||
for symbol < 0x100 {
|
||||
i := rd.decodeBit(lsc.coders, symbol)
|
||||
symbol = symbol<<1 | i
|
||||
}
|
||||
return byte(symbol)
|
||||
}
|
||||
|
||||
func (lsc *litSubCoder) decodeWithMatchByte(rd *rangeDecoder, matchByte byte) byte {
|
||||
uMatchByte := uint32(matchByte)
|
||||
symbol := uint32(1)
|
||||
for symbol < 0x100 {
|
||||
matchBit := (uMatchByte >> 7) & 1
|
||||
uMatchByte <<= 1
|
||||
bit := rd.decodeBit(lsc.coders, ((1+matchBit)<<8)+symbol)
|
||||
symbol = (symbol << 1) | bit
|
||||
if matchBit != bit {
|
||||
for symbol < 0x100 {
|
||||
i := rd.decodeBit(lsc.coders, symbol)
|
||||
symbol = (symbol << 1) | i
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return byte(symbol)
|
||||
}
|
||||
|
||||
|
||||
func (lsc *litSubCoder) encode(re *rangeEncoder, symbol byte) {
|
||||
uSymbol := uint32(symbol)
|
||||
context := uint32(1)
|
||||
for i := uint32(7); int32(i) >= 0; i-- {
|
||||
bit := (uSymbol >> i) & 1
|
||||
re.encode(lsc.coders, context, bit)
|
||||
context = context<<1 | bit
|
||||
}
|
||||
}
|
||||
|
||||
func (lsc *litSubCoder) encodeMatched(re *rangeEncoder, matchByte, symbol byte) {
|
||||
uMatchByte := uint32(matchByte)
|
||||
uSymbol := uint32(symbol)
|
||||
context := uint32(1)
|
||||
same := true
|
||||
for i := uint32(7); int32(i) >= 0; i-- {
|
||||
bit := (uSymbol >> i) & 1
|
||||
state := context
|
||||
if same == true {
|
||||
matchBit := (uMatchByte >> i) & 1
|
||||
state += (1 + matchBit) << 8
|
||||
same = false
|
||||
if matchBit == bit {
|
||||
same = true
|
||||
}
|
||||
}
|
||||
re.encode(lsc.coders, state, bit)
|
||||
context = context<<1 | bit
|
||||
}
|
||||
}
|
||||
|
||||
func (lsc *litSubCoder) getPrice(matchMode bool, matchByte, symbol byte) uint32 {
|
||||
uMatchByte := uint32(matchByte)
|
||||
uSymbol := uint32(symbol)
|
||||
price := uint32(0)
|
||||
context := uint32(1)
|
||||
i := uint32(7)
|
||||
if matchMode == true {
|
||||
for ; int32(i) >= 0; i-- {
|
||||
matchBit := (uMatchByte >> i) & 1
|
||||
bit := (uSymbol >> i) & 1
|
||||
price += getPrice(lsc.coders[(1+matchBit)<<8+context], bit)
|
||||
context = context<<1 | bit
|
||||
if matchBit != bit {
|
||||
i--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for ; int32(i) >= 0; i-- {
|
||||
bit := (uSymbol >> i) & 1
|
||||
price += getPrice(lsc.coders[context], bit)
|
||||
context = context<<1 | bit
|
||||
}
|
||||
return price
|
||||
}
|
||||
|
||||
|
||||
type litCoder struct {
|
||||
coders []*litSubCoder
|
||||
numPrevBits uint32 // literal context bits // lc
|
||||
// numPosBits uint32 // literal position state bits // lp
|
||||
posMask uint32
|
||||
}
|
||||
|
||||
func newLitCoder(numPosBits, numPrevBits uint32) *litCoder {
|
||||
numStates := uint32(1) << (numPrevBits + numPosBits)
|
||||
lc := &litCoder{
|
||||
coders: make([]*litSubCoder, numStates),
|
||||
numPrevBits: numPrevBits,
|
||||
// numPosBits: numPosBits,
|
||||
posMask: (1 << numPosBits) - 1,
|
||||
}
|
||||
for i := uint32(0); i < numStates; i++ {
|
||||
lc.coders[i] = newLitSubCoder()
|
||||
}
|
||||
return lc
|
||||
}
|
||||
|
||||
func (lc *litCoder) getSubCoder(pos uint32, prevByte byte) *litSubCoder {
|
||||
return lc.coders[((pos&lc.posMask)<<lc.numPrevBits)+uint32(prevByte>>(8-lc.numPrevBits))]
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
type rangeBitTreeCoder struct {
|
||||
models []uint16 // length(models) is at most 1<<8
|
||||
numBitLevels uint32 // min 2; max 8
|
||||
}
|
||||
|
||||
func newRangeBitTreeCoder(numBitLevels uint32) *rangeBitTreeCoder {
|
||||
return &rangeBitTreeCoder{
|
||||
numBitLevels: numBitLevels,
|
||||
models: initBitModels(1 << numBitLevels),
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) decode(rd *rangeDecoder) (res uint32) {
|
||||
res = 1
|
||||
for bitIndex := rc.numBitLevels; bitIndex != 0; bitIndex-- {
|
||||
bit := rd.decodeBit(rc.models, res)
|
||||
res = res<<1 + bit
|
||||
}
|
||||
res -= 1 << rc.numBitLevels
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) reverseDecode(rd *rangeDecoder) (res uint32) {
|
||||
index := uint32(1)
|
||||
res = 0
|
||||
for bitIndex := uint32(0); bitIndex < rc.numBitLevels; bitIndex++ {
|
||||
bit := rd.decodeBit(rc.models, index)
|
||||
index <<= 1
|
||||
index += bit
|
||||
res |= bit << bitIndex
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func reverseDecodeIndex(rd *rangeDecoder, models []uint16, startIndex, numBitModels uint32) (res uint32) {
|
||||
index := uint32(1)
|
||||
res = 0
|
||||
for bitIndex := uint32(0); bitIndex < numBitModels; bitIndex++ {
|
||||
bit := rd.decodeBit(models, startIndex+index)
|
||||
index <<= 1
|
||||
index += bit
|
||||
res |= bit << bitIndex
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) encode(re *rangeEncoder, symbol uint32) {
|
||||
m := uint32(1)
|
||||
for bitIndex := rc.numBitLevels; bitIndex != 0; {
|
||||
bitIndex--
|
||||
bit := (symbol >> bitIndex) & 1
|
||||
re.encode(rc.models, m, bit)
|
||||
m = m<<1 | bit
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) reverseEncode(re *rangeEncoder, symbol uint32) {
|
||||
m := uint32(1)
|
||||
for i := uint32(0); i < rc.numBitLevels; i++ {
|
||||
bit := symbol & 1
|
||||
re.encode(rc.models, m, bit)
|
||||
m = m<<1 | bit
|
||||
symbol >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) getPrice(symbol uint32) (res uint32) {
|
||||
res = 0
|
||||
m := uint32(1)
|
||||
for bitIndex := rc.numBitLevels; bitIndex != 0; {
|
||||
bitIndex--
|
||||
bit := (symbol >> bitIndex) & 1
|
||||
res += getPrice(rc.models[m], bit)
|
||||
m = m<<1 + bit
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *rangeBitTreeCoder) reverseGetPrice(symbol uint32) (res uint32) {
|
||||
res = 0
|
||||
m := uint32(1)
|
||||
for i := rc.numBitLevels; i != 0; i-- {
|
||||
bit := symbol & 1
|
||||
symbol >>= 1
|
||||
res += getPrice(rc.models[m], bit)
|
||||
m = m<<1 | bit
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func reverseGetPriceIndex(models []uint16, startIndex, numBitLevels, symbol uint32) (res uint32) {
|
||||
res = 0
|
||||
m := uint32(1)
|
||||
for i := numBitLevels; i != 0; i-- {
|
||||
bit := symbol & 1
|
||||
symbol >>= 1
|
||||
res += getPrice(models[startIndex+m], bit)
|
||||
m = m<<1 | bit
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func reverseEncodeIndex(re *rangeEncoder, models []uint16, startIndex, numBitLevels, symbol uint32) {
|
||||
m := uint32(1)
|
||||
for i := uint32(0); i < numBitLevels; i++ {
|
||||
bit := symbol & 1
|
||||
re.encode(models, startIndex+m, bit)
|
||||
m = m<<1 | bit
|
||||
symbol >>= 1
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
kTopValue = 1 << 24
|
||||
kNumBitModelTotalBits = 11
|
||||
kBitModelTotal = 1 << kNumBitModelTotalBits
|
||||
kNumMoveBits = 5
|
||||
)
|
||||
|
||||
// The actual read interface needed by NewDecoder. If the passed in io.Reader
|
||||
// does not also have ReadByte, the NewDecoder will introduce its own buffering.
|
||||
//
|
||||
type Reader interface {
|
||||
io.Reader
|
||||
ReadByte() (c byte, err error)
|
||||
}
|
||||
|
||||
type rangeDecoder struct {
|
||||
r Reader
|
||||
rrange uint32
|
||||
code uint32
|
||||
}
|
||||
|
||||
func makeReader(r io.Reader) Reader {
|
||||
if rr, ok := r.(Reader); ok {
|
||||
return rr
|
||||
}
|
||||
return bufio.NewReader(r)
|
||||
}
|
||||
|
||||
func newRangeDecoder(r io.Reader) *rangeDecoder {
|
||||
rd := &rangeDecoder{
|
||||
r: makeReader(r),
|
||||
rrange: 0xFFFFFFFF,
|
||||
code: 0,
|
||||
}
|
||||
buf := make([]byte, 5)
|
||||
n, err := rd.r.Read(buf)
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
if n != len(buf) {
|
||||
throw(nReadError)
|
||||
}
|
||||
for i := 0; i < len(buf); i++ {
|
||||
rd.code = rd.code<<8 | uint32(buf[i])
|
||||
}
|
||||
return rd
|
||||
}
|
||||
|
||||
func (rd *rangeDecoder) decodeDirectBits(numTotalBits uint32) (res uint32) {
|
||||
for i := numTotalBits; i != 0; i-- {
|
||||
rd.rrange >>= 1
|
||||
t := (rd.code - rd.rrange) >> 31
|
||||
rd.code -= rd.rrange & (t - 1)
|
||||
res = res<<1 | (1 - t)
|
||||
if rd.rrange < kTopValue {
|
||||
c, err := rd.r.ReadByte()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
rd.code = rd.code<<8 | uint32(c)
|
||||
rd.rrange <<= 8
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rd *rangeDecoder) decodeBit(probs []uint16, index uint32) (res uint32) {
|
||||
prob := probs[index]
|
||||
newBound := (rd.rrange >> kNumBitModelTotalBits) * uint32(prob)
|
||||
if rd.code < newBound {
|
||||
rd.rrange = newBound
|
||||
probs[index] = prob + (kBitModelTotal-prob)>>kNumMoveBits
|
||||
if rd.rrange < kTopValue {
|
||||
b, err := rd.r.ReadByte()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
rd.code = rd.code<<8 | uint32(b)
|
||||
rd.rrange <<= 8
|
||||
}
|
||||
res = 0
|
||||
} else {
|
||||
rd.rrange -= newBound
|
||||
rd.code -= newBound
|
||||
probs[index] = prob - prob>>kNumMoveBits
|
||||
if rd.rrange < kTopValue {
|
||||
b, err := rd.r.ReadByte()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
rd.code = rd.code<<8 | uint32(b)
|
||||
rd.rrange <<= 8
|
||||
}
|
||||
res = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func initBitModels(length uint32) (probs []uint16) {
|
||||
probs = make([]uint16, length)
|
||||
val := uint16(kBitModelTotal) >> 1
|
||||
for i := uint32(0); i < length; i++ {
|
||||
probs[i] = val // 1 << 10
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
kNumMoveReducingBits = 2
|
||||
kNumBitPriceShiftBits = 6
|
||||
)
|
||||
|
||||
// The actual write interface needed by NewEncoder. If the passed in io.Writer
|
||||
// does not also have WriteByte and Flush, the NewEncoder will wrap it into an
|
||||
// bufio.Writer.
|
||||
//
|
||||
type Writer interface {
|
||||
io.Writer
|
||||
Flush() error
|
||||
WriteByte(c byte) error
|
||||
}
|
||||
|
||||
type rangeEncoder struct {
|
||||
w Writer
|
||||
low uint64
|
||||
pos uint64
|
||||
cacheSize uint32
|
||||
cache uint32
|
||||
rrange uint32
|
||||
}
|
||||
|
||||
func makeWriter(w io.Writer) Writer {
|
||||
if ww, ok := w.(Writer); ok {
|
||||
return ww
|
||||
}
|
||||
return bufio.NewWriter(w)
|
||||
}
|
||||
|
||||
func newRangeEncoder(w io.Writer) *rangeEncoder {
|
||||
return &rangeEncoder{
|
||||
w: makeWriter(w),
|
||||
low: 0,
|
||||
pos: 0,
|
||||
cacheSize: 1,
|
||||
cache: 0,
|
||||
rrange: 0xFFFFFFFF,
|
||||
}
|
||||
}
|
||||
|
||||
func (re *rangeEncoder) flush() {
|
||||
for i := 0; i < 5; i++ {
|
||||
re.shiftLow()
|
||||
}
|
||||
err := re.w.Flush()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (re *rangeEncoder) shiftLow() {
|
||||
lowHi := uint32(re.low >> 32)
|
||||
if lowHi != 0 || re.low < uint64(0x00000000FF000000) {
|
||||
re.pos += uint64(re.cacheSize)
|
||||
temp := re.cache
|
||||
dwtemp := uint32(1) // execute the loop at least once (do-while)
|
||||
for ; dwtemp != 0; dwtemp = re.cacheSize {
|
||||
err := re.w.WriteByte(byte(temp + lowHi))
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
temp = 0x000000FF
|
||||
re.cacheSize--
|
||||
}
|
||||
re.cache = uint32(re.low) >> 24
|
||||
}
|
||||
re.cacheSize++
|
||||
re.low = uint64(uint32(re.low) << 8)
|
||||
}
|
||||
|
||||
func (re *rangeEncoder) encodeDirectBits(v, numTotalBits uint32) {
|
||||
for i := numTotalBits - 1; int32(i) >= 0; i-- {
|
||||
re.rrange >>= 1
|
||||
if (v>>i)&1 == 1 {
|
||||
re.low += uint64(re.rrange)
|
||||
}
|
||||
if re.rrange < kTopValue {
|
||||
re.rrange <<= 8
|
||||
re.shiftLow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (re *rangeEncoder) processedSize() uint64 {
|
||||
return uint64(re.cacheSize) + re.pos + 4
|
||||
}
|
||||
|
||||
func (re *rangeEncoder) encode(probs []uint16, index, symbol uint32) {
|
||||
prob := probs[index]
|
||||
newBound := (re.rrange >> kNumBitModelTotalBits) * uint32(prob)
|
||||
if symbol == 0 {
|
||||
re.rrange = newBound
|
||||
probs[index] = prob + (kBitModelTotal-prob)>>kNumMoveBits
|
||||
} else {
|
||||
re.low += uint64(newBound) & uint64(0xFFFFFFFFFFFFFFFF)
|
||||
re.rrange -= newBound
|
||||
probs[index] = prob - prob>>kNumMoveBits
|
||||
}
|
||||
if re.rrange < kTopValue {
|
||||
re.rrange <<= 8
|
||||
re.shiftLow()
|
||||
}
|
||||
}
|
||||
|
||||
var probPrices []uint32 = make([]uint32, kBitModelTotal>>kNumMoveReducingBits) // len(probPrices) = 512
|
||||
|
||||
// should be called in the encoder's contructor.
|
||||
func initProbPrices() {
|
||||
kNumBits := uint32(kNumBitModelTotalBits - kNumMoveReducingBits)
|
||||
for i := kNumBits - 1; int32(i) >= 0; i-- {
|
||||
start := uint32(1) << (kNumBits - i - 1)
|
||||
end := uint32(1) << (kNumBits - i)
|
||||
for j := start; j < end; j++ {
|
||||
probPrices[j] = i<<kNumBitPriceShiftBits + ((end-j)<<kNumBitPriceShiftBits)>>(kNumBits-i-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPrice(prob uint16, symbol uint32) uint32 {
|
||||
return probPrices[(((uint32(prob)-symbol)^(-symbol))&(uint32(kBitModelTotal)-1))>>kNumMoveReducingBits]
|
||||
}
|
||||
|
||||
func getPrice0(prob uint16) uint32 {
|
||||
return probPrices[prob>>kNumMoveReducingBits]
|
||||
}
|
||||
|
||||
func getPrice1(prob uint16) uint32 {
|
||||
return probPrices[(kBitModelTotal-prob)>>kNumMoveReducingBits]
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2010, Andrei Vieru. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package lzma
|
||||
|
||||
func minInt32(left int32, right int32) int32 {
|
||||
if left < right {
|
||||
return left
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
func minUInt32(left uint32, right uint32) uint32 {
|
||||
if left < right {
|
||||
return left
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
func maxInt32(left int32, right int32) int32 {
|
||||
if left > right {
|
||||
return left
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
func maxUInt32(left uint32, right uint32) uint32 {
|
||||
if left > right {
|
||||
return left
|
||||
}
|
||||
return right
|
||||
}
|
||||
Reference in New Issue
Block a user