Skip to content

Commit b08fc07

Browse files
author
Tobias Seipke
committed
added utf16 functionality (compressToUTF16 / decompressFromUTF16)
1 parent 9c2221c commit b08fc07

11 files changed

Lines changed: 440 additions & 128 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ composer require nullpunkt/lz-string-php
2424
```
2525

2626
## Changelog
27+
### 2016-03-23
28+
- v1.2.0 Added utf16 functionality
29+
2730
### 2016-02-28
2831
- v1.1.0 Completely rewritten LZString component to match the output of js-lz-string version 1.4.4
2932
- PHPUnit tests for continuous testing / comparision of lz-string js

boilerplate/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
lz-string-php boilerplate
2+
=========================
3+
4+
HTML Boilerplate to test the php implementiation of lz-string manually
5+
6+
## Usage
7+
```cmd
8+
php -S localhost:8000
9+
```

boilerplate/index.php

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
}
1616
</style>
1717
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap-theme.min.css">
18-
1918
</head>
2019
<body>
2120
@@ -30,52 +29,86 @@
3029
</div>
3130
<button type="submit" class="btn btn-primary" data-ng-click="vm.encode()">Encode!</button>
3231
&nbsp;&nbsp;&nbsp;&nbsp;
33-
<input id="display-compressed" type="checkbox" class="form-control" data-ng-model="vm.displayCompressed">
34-
<label for="display-compressed">Display Compressed Bytes</label>
32+
<span data-ng-if="vm.activeTab == 'compress64'">
33+
<input id="display-compressed" type="checkbox" class="form-control" data-ng-model="vm.displayCompressed">
34+
<label for="display-compressed">Display Compressed Bytes</label>
35+
</span>
3536
</form>
3637
</div>
3738
<br>
3839
<br>
39-
<hr>
4040
<br>
41-
<div class="col-md-12">
42-
<table class="table table-condensed">
43-
<thead>
44-
<tr>
45-
<th>Source</th>
46-
<th data-ng-show="vm.displayCompressed">Compressed</th>
47-
<th data-ng-show="vm.displayCompressed">Compressed [Bytes]</th>
48-
<th>Compressed64</th>
49-
<th>Decompressed</th>
50-
<th>Decompressed64</th>
51-
</tr>
52-
</thead>
53-
<tbody>
54-
<tr data-ng-repeat="row in vm.results | orderBy:'$index':true">
55-
<td data-ng-bind="row.input"></td>
56-
<td data-ng-show="vm.displayCompressed" ng-class="{warning: row.compressed!=row.php.compressed}">
57-
<div data-ng-bind="row.compressed"></div>
58-
<div data-ng-bind="row.php.compressed"></div>
59-
</td>
60-
<td data-ng-show="vm.displayCompressed" ng-class="{warning: row.compressedBytesString!=row.php.compressedBytesString}">
61-
<div data-ng-bind="row.compressedBytesString"></div>
62-
<div data-ng-bind="row.php.compressedBytesString"></div>
63-
</td>
64-
<td ng-class="{warning: row.compressed64!=row.php.compressed64}">
65-
<div data-ng-bind="row.compressed64"></div>
66-
<div data-ng-bind="row.php.compressed64"></div>
67-
</td>
68-
<td ng-class="{warning: row.decompressed!=row.php.decompressed}">
69-
<div data-ng-bind="row.decompressed"></div>
70-
<div data-ng-bind="row.php.decompressed"></div>
71-
</td>
72-
<td ng-class="{warning: row.decompressed64!=row.php.decompressed64}">
73-
<div data-ng-bind="row.decompressed64"></div>
74-
<div data-ng-bind="row.php.decompressed64"></div>
75-
</td>
76-
</tr>
77-
</tbody>
78-
</table>
41+
<ul class="nav nav-tabs">
42+
<li data-ng-class="{active: (vm.activeTab == 'compress64')}"><a href data-ng-click="vm.activeTab = 'compress64'">Compressed64</a></li>
43+
<li data-ng-class="{active: (vm.activeTab == 'utf16')}"><a href data-ng-click="vm.activeTab = 'utf16'">UTF-16</a></li>
44+
</ul>
45+
<br>
46+
<div data-ng-if="vm.activeTab == 'compress64'">
47+
<div class="col-md-12">
48+
<table class="table table-condensed">
49+
<thead>
50+
<tr>
51+
<th>Source</th>
52+
<th data-ng-show="vm.displayCompressed">Compressed</th>
53+
<th data-ng-show="vm.displayCompressed">Compressed [Bytes]</th>
54+
<th>Compressed64</th>
55+
<th>Decompressed</th>
56+
<th>Decompressed64</th>
57+
</tr>
58+
</thead>
59+
<tbody>
60+
<tr data-ng-repeat="row in vm.results | orderBy:'$index':true">
61+
<td data-ng-bind="row.input"></td>
62+
<td data-ng-show="vm.displayCompressed" ng-class="{warning: row.compressed!=row.php.compressed}">
63+
<div data-ng-bind="row.compressed"></div>
64+
<div data-ng-bind="row.php.compressed"></div>
65+
</td>
66+
<td data-ng-show="vm.displayCompressed" ng-class="{warning: row.compressedBytesString!=row.php.compressedBytesString}">
67+
<div data-ng-bind="row.compressedBytesString"></div>
68+
<div data-ng-bind="row.php.compressedBytesString"></div>
69+
</td>
70+
<td ng-class="{warning: row.compressed64!=row.php.compressed64}">
71+
<div data-ng-bind="row.compressed64"></div>
72+
<div data-ng-bind="row.php.compressed64"></div>
73+
</td>
74+
<td ng-class="{warning: row.decompressed!=row.php.decompressed}">
75+
<div data-ng-bind="row.decompressed"></div>
76+
<div data-ng-bind="row.php.decompressed"></div>
77+
</td>
78+
<td ng-class="{warning: row.decompressed64!=row.php.decompressed64}">
79+
<div data-ng-bind="row.decompressed64"></div>
80+
<div data-ng-bind="row.php.decompressed64"></div>
81+
</td>
82+
</tr>
83+
</tbody>
84+
</table>
85+
</div>
86+
</div>
87+
<div data-ng-if="vm.activeTab == 'utf16'">
88+
<div class="col-md-12">
89+
<table class="table table-condensed">
90+
<thead>
91+
<tr>
92+
<th>Source</th>
93+
<th>Compressed [Bytes]</th>
94+
<th>Decompressed</th>
95+
</tr>
96+
</thead>
97+
<tbody>
98+
<tr data-ng-repeat="row in vm.results16 | orderBy:'$index':true">
99+
<td data-ng-bind="row.input"></td>
100+
<td ng-class="{warning: row.compressedBytesString!=row.php.compressedBytesString}">
101+
<div data-ng-bind="row.compressedBytesString"></div>
102+
<div data-ng-bind="row.php.compressedBytesString"></div>
103+
</td>
104+
<td ng-class="{warning: row.decompressed!=row.php.decompressed}">
105+
<div data-ng-bind="row.decompressed"></div>
106+
<div data-ng-bind="row.php.decompressed"></div>
107+
</td>
108+
</tr>
109+
</tbody>
110+
</table>
111+
</div>
79112
</div>
80113
</div>
81114
</div>

boilerplate/main.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@ angular.module('lzapp').controller('LZStringCtrl', LZStringCtrl);
55
LZStringCtrl.$inject = ['$http'];
66

77
function LZStringCtrl($http) {
8-
var vm = this;
8+
var vm = this,
9+
// def = 'متن شگفت انگیز در اینجا'
10+
def = ' «sauvegardes», «'
11+
;
912

10-
vm.source = '';
13+
vm.source = def;
1114
vm.displayCompressed = false;
1215
vm.results = [];
13-
16+
vm.results16 = [];
17+
vm.activeTab = 'utf16';
1418

1519
vm.encode = encode;
1620
vm.encodeLong = encodeLong;
1721

1822
vm.longResult = null;
1923

2024
function encode() {
21-
vm.results.push(generate(vm.source));
22-
vm.source = '';
25+
if(vm.activeTab == 'utf16') {
26+
vm.results16.push(generate16(vm.source));
27+
} else {
28+
vm.results.push(generate(vm.source));
29+
}
30+
vm.source = def;
2331
}
2432

2533
function encodeLong() {
@@ -65,6 +73,30 @@ function LZStringCtrl($http) {
6573
return result;
6674
}
6775

76+
function generate16(str) {
77+
var com = LZString.compressToUTF16(str), compressedBytes = bytes(com);
78+
var result = {
79+
input: str,
80+
compressed: com,
81+
compressedBytes: compressedBytes,
82+
compressedBytesString: bytesToHexStr(compressedBytes),
83+
decompressed: LZString.decompressFromUTF16(com),
84+
php: {
85+
86+
}
87+
};
88+
89+
$http.post('service16.php', {str: str}).then(function(res) {
90+
result.php = res.data;
91+
if(angular.isDefined(result.php.compressedBytes)) {
92+
// result.php.compressedBytesString = result.php.compressedBytes;
93+
result.php.compressedBytesString = bytesToHexStr(result.php.compressedBytes)
94+
}
95+
});
96+
97+
return result;
98+
}
99+
68100
function bytes(str) {
69101
var bytes = [];
70102
for (var i = 0; i < str.length; ++i) {

boilerplate/service16.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
require_once '../vendor/autoload.php';
3+
4+
use \LZCompressor\LZString as LZ;
5+
use \LZCompressor\LZUtil16 as Util;
6+
7+
$log = new \Monolog\Logger('name');
8+
\Monolog\ErrorHandler::register($log);
9+
10+
$log->pushHandler(new \Monolog\Handler\StreamHandler(getcwd().'/log/service.log'));
11+
$request = json_decode(file_get_contents('php://input'), true);
12+
13+
14+
15+
$log->debug('Request: '.json_encode($request));
16+
17+
$compressed = LZ::compressToUTF16($request['str']);
18+
19+
$length = Util::utf16_strlen($compressed);
20+
$compressedBytes = [];
21+
for($i=0; $i<$length; $i++) {
22+
$val = Util::charCodeAt($compressed, $i);
23+
$log->debug('val: |'.$val.'|');
24+
$compressedBytes[] = $val & 255;
25+
$compressedBytes[] = ($val>>8) & 255;
26+
}
27+
28+
29+
$result = [
30+
'compressedBytes' => $compressedBytes,
31+
'decompressed' => LZ::decompressFromUTF16($compressed)
32+
];
33+
34+
$log->debug('Result: '.json_encode($result));
35+
36+
header('Content-type: text/application');
37+
echo json_encode($result);
38+
//echo json_encode(['hi']);

src/LZCompressor/LZString.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ public static function decompressFromBase64($input)
2525
});
2626
}
2727

28+
public static function compressToUTF16($input) {
29+
return self::_compress($input, 15, function($a) {
30+
return LZUtil16::fromCharCode($a+32);
31+
}) . LZUtil16::utf16_chr(32);
32+
}
33+
34+
public static function decompressFromUTF16($input) {
35+
return self::_decompress($input, 16384, function($feed, $index) {
36+
return LZUtil16::charCodeAt($feed, $index)-32;
37+
});
38+
}
39+
2840
/**
2941
* @param string $uncompressed
3042
* @return string
@@ -273,7 +285,7 @@ private static function _decompress($compressed, $resetValue, $getNextValue)
273285
}
274286

275287
$result .= $entry;
276-
$dictionary->addEntry($w . $entry{0});
288+
$dictionary->addEntry($w . LZUtil::utf8_charAt($entry, 0));
277289
$w = $entry;
278290

279291
$enlargeIn--;

src/LZCompressor/LZUtil.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,4 @@ public static function utf8_charAt($str, $num)
107107
public static function utf8_strlen($str) {
108108
return mb_strlen($str, 'UTF-8');
109109
}
110-
111-
112110
}

src/LZCompressor/LZUtil16.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: sics
5+
* Date: 27.02.2016
6+
* Time: 15:54
7+
*/
8+
9+
namespace LZCompressor;
10+
11+
12+
class LZUtil16
13+
{
14+
15+
/**
16+
* @return string
17+
*/
18+
public static function fromCharCode()
19+
{
20+
return array_reduce(func_get_args(), function ($a, $b) {
21+
$a .= self::utf16_chr($b);
22+
return $a;
23+
});
24+
}
25+
26+
/**
27+
* Phps chr() equivalent for UTF-16 encoding
28+
*
29+
* @param int|string $u
30+
* @return string
31+
*/
32+
public static function utf16_chr($u)
33+
{
34+
return mb_convert_encoding('&#' . intval($u) . ';', 'UTF-16', 'HTML-ENTITIES');
35+
}
36+
37+
/**
38+
* @param string $str
39+
* @param int $num
40+
*
41+
* @return bool|integer
42+
*/
43+
public static function charCodeAt($str, $num=0)
44+
{
45+
return self::utf16_ord(self::utf16_charAt($str, $num));
46+
}
47+
48+
/**
49+
* @source http://blog.sarabande.jp/post/35970262740
50+
* @param string $ch
51+
* @return bool|integer
52+
*/
53+
function utf16_ord($ch) {
54+
$length = strlen($ch);
55+
if (2 === $length) {
56+
return hexdec(bin2hex($ch));
57+
} else if (4 === $length) {
58+
$w1 = $ch[0].$ch[1];
59+
$w2 = $ch[2].$ch[3];
60+
if ($w1 < "\xD8\x00" || "\xDF\xFF" < $w1 || $w2 < "\xDC\x00" || "\xDF\xFF" < $w2) {
61+
return false;
62+
}
63+
$w1 = (hexdec(bin2hex($w1)) & 0x3ff) << 10;
64+
$w2 = hexdec(bin2hex($w2)) & 0x3ff;
65+
return $w1 + $w2 + 0x10000;
66+
}
67+
return false;
68+
}
69+
70+
/**
71+
* @param string $str
72+
* @param integer $num
73+
*
74+
* @return string
75+
*/
76+
public static function utf16_charAt($str, $num)
77+
{
78+
return mb_substr($str, $num, 1, 'UTF-16');
79+
}
80+
81+
/**
82+
* @param string $str
83+
* @return integer
84+
*/
85+
public static function utf16_strlen($str)
86+
{
87+
return mb_strlen($str, 'UTF-16');
88+
}
89+
90+
}

0 commit comments

Comments
 (0)