Skip to content

Commit

Permalink
Finished a basic implementation in Ruby, adding it to the build.
Browse files Browse the repository at this point in the history
  • Loading branch information
olle committed Feb 8, 2010
1 parent b9e0bf6 commit 309e054
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 12 deletions.
1 change: 1 addition & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
- PHP
- Python
- Java
- Ruby

[A language missing? Please add it to the project.]

Expand Down
8 changes: 2 additions & 6 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -202,21 +202,17 @@
<fail message="Ruby unit-test failed: ${rubyunittestresults}">
<condition>
<not>
<matches pattern="OK"
<matches pattern="0 failures, 0 errors"
string="${rubyunittestresults}" />
</not>
</condition>
</fail>
<echo>Ruby unit-tests: OK</echo>
<echo>Ruby Unit Test: OK</echo>
</sequential>
</macrodef>

<!-- TARGETS -->

<target name="rbtest">
<rubyunittest />
</target>

<target name="test" description="Runs the project unit tests">
<phpunit testcase="LZ77Test" file="${src.dir}/test/php/lz77Test.php" />
<jslint files="${src.dir}/main/js/lz77.js" />
Expand Down
132 changes: 129 additions & 3 deletions src/main/ruby/lz77.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,138 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @author Olle Törnström olle[at]studiomediatech[dot]com
# @created 2010-02-07
#
class Compressor
def compress(data)
return "TODO: Implement!"

def initialize
@referencePrefix = "`"
@referencePrefixCode = @referencePrefix[0]
@referenceIntBase = 96
@referenceIntFloorCode = " "[0]
@referenceIntCeilCode = @referenceIntFloorCode + @referenceIntBase - 1
@maxStringDistance = @referenceIntBase ** 2 - 1
@minStringLength = 5
@maxStringLength = @referenceIntBase ** 1 - 1 + @minStringLength
@maxWindowLength = @maxStringDistance + @minStringLength;
@defaultWindowLength = 144
end

def compress(data, windowLength = @defaultWindowLength)
compressed = ""
pos = 0
lastPos = data.length - @minStringLength
while pos < lastPos do
searchStart = [pos - windowLength, 0].max
matchLength = @minStringLength
foundMatch = false
bestMatchDistance = @maxStringDistance
bestMatchLength = 0
newCompressed = nil
while (searchStart + matchLength) < pos do
m1 = data[searchStart, matchLength]
m2 = data[pos, matchLength]
isValidMatch = (m1 == m2 and matchLength < @maxStringLength)
if isValidMatch then
matchLength += 1
foundMatch = true
else
realMatchLength = matchLength - 1
if foundMatch and realMatchLength > bestMatchLength then
bestMatchDistance = pos - searchStart - realMatchLength
bestMatchLength = realMatchLength
end
matchLength = @minStringLength
searchStart += 1
foundMatch = false
end
end
if bestMatchLength > 0 then
head = "" << @referencePrefix
head << encodeReferenceInt(bestMatchDistance, 2)
tail = encodeReferenceLength(bestMatchLength)
newCompressed = ""
newCompressed = head << tail
pos += bestMatchLength
else
if data[pos, 1] != @referencePrefix then
newCompressed = data[pos, 1]
else
newCompressed = @referencePrefix << @referencePrefix
end
pos += 1
end
compressed << newCompressed
end
return compressed + data[pos..-1].gsub(/`/, "``")
end

def decompress(data)
return "TODO: Implement!"
decompressed = ""
pos = 0
while pos < data.length do
currentChar = data[pos, 1]
if currentChar != @referencePrefix then
decompressed << currentChar
pos += 1
else
nextChar = data[pos + 1, 1]
if nextChar != @referencePrefix then
distance = decodeReferenceInt(data[pos + 1, 2], 2)
length = decodeReferenceLength(data[pos + 3, 1])
start = decompressed.length - distance - length
stop = start + length
decompressed << decompressed[start..stop-1]
pos += @minStringLength - 1
else
decompressed << @referencePrefix
pos += 2
end
end
end
return decompressed
end

private # PRIVATE METHODS **************************************************

def encodeReferenceInt(value, width)
encoded = ""
if value >= 0 and value < ((@referenceIntBase ** width) - 1) then
while value > 0 do
encoded = ((value % @referenceIntBase) + @referenceIntFloorCode).chr << encoded
value = value / @referenceIntBase
end
missingLength = width - encoded.length
(0..missingLength-1).each do
encoded = (@referenceIntFloorCode).chr << encoded
end
return encoded
else
raise "Reference value out of range"
end
end

def encodeReferenceLength(length)
return encodeReferenceInt(length - @minStringLength, 1)
end

def decodeReferenceInt(data, width)
value = 0
(0..width-1).each do |i|
value *= @referenceIntBase
charCode = data[i]
if charCode >= @referenceIntFloorCode and charCode <= @referenceIntCeilCode then
value += (charCode - @referenceIntFloorCode)
else
raise "Invalid char code"
end
end
return value
end

def decodeReferenceLength(data)
return decodeReferenceInt(data, 1) + @minStringLength
end

end
2 changes: 1 addition & 1 deletion src/test/php/lz77Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class LZ77Test extends PHPUnit_Framework_TestCase {
protected $encodedTextData = "ababassbabasbabbba` '&bs` 1 sb` 4!` . abaaaa` *!";

protected function setUp() {

$this->compressor = new LZ77();
}

Expand All @@ -27,4 +26,5 @@ public function testDecompress() {
$this->assertEquals($this->textData, $result);
}
}

?>
6 changes: 4 additions & 2 deletions src/test/ruby/lz77test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ def setup
end

def testCompress
assert_equal(@encodedTextData, @compressor.compress(@textData))
result = @compressor.compress(@textData)
assert_equal(@encodedTextData, result)
end

def testDecompress
assert_equal(@textData, @compressor.decompress(@encodedTextData))
result = @compressor.decompress(@encodedTextData)
assert_equal(@textData, result)
end
end

0 comments on commit 309e054

Please sign in to comment.