Skip to content

Commit 3613207

Browse files
authored
Merge pull request #3 from source-c/trunk
Trunk -> Master
2 parents 88a2de1 + de35aef commit 3613207

File tree

9 files changed

+266
-28
lines changed

9 files changed

+266
-28
lines changed

.github/workflows/clojure-master.yml

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,39 @@ name: Clojure CI for master
22

33
on:
44
push:
5-
branches: [ master ]
5+
branches: [ trunk ]
66
pull_request:
7-
branches: [ master ]
7+
branches: [ trunk ]
88

99
jobs:
1010
build:
11-
1211
runs-on: ubuntu-latest
1312

1413
steps:
15-
- uses: actions/checkout@v2
16-
- name: Install dependencies
17-
run: lein deps
18-
- name: Run tests
19-
run: lein test
14+
- name: Check out repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Java
18+
uses: actions/setup-java@v4
19+
with:
20+
distribution: 'temurin'
21+
java-version: '17'
22+
23+
- name: Install Leiningen
24+
run: |
25+
sudo apt-get update
26+
sudo apt-get install -y leiningen
27+
28+
- name: Cache Maven dependencies
29+
uses: actions/cache@v4
30+
with:
31+
path: ~/.m2/repository
32+
key: ${{ runner.os }}-lein-${{ hashFiles('project.clj') }}
33+
restore-keys: |
34+
${{ runner.os }}-lein-
35+
36+
- name: Install dependencies
37+
run: lein deps
38+
39+
- name: Run tests
40+
run: lein test

.github/workflows/clojure-trunk.yml

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,33 @@ on:
88

99
jobs:
1010
build:
11-
1211
runs-on: ubuntu-latest
1312

1413
steps:
15-
- uses: actions/checkout@v2
16-
- name: Install dependencies
17-
run: lein deps
18-
- name: Run tests
19-
run: lein test
14+
- name: Check out repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Java
18+
uses: actions/setup-java@v4
19+
with:
20+
distribution: 'temurin'
21+
java-version: '21'
22+
23+
- name: Install Leiningen
24+
run: |
25+
sudo apt-get update
26+
sudo apt-get install -y leiningen
27+
28+
- name: Cache Maven dependencies
29+
uses: actions/cache@v4
30+
with:
31+
path: ~/.m2/repository
32+
key: ${{ runner.os }}-lein-${{ hashFiles('project.clj') }}
33+
restore-keys: |
34+
${{ runner.os }}-lein-
35+
36+
- name: Install dependencies
37+
run: lein deps
38+
39+
- name: Run tests
40+
run: lein test

README.adoc

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,33 @@ CAUTION: From version v0.3.2 and upward the library may require Java class versi
9696
=> 152961502
9797
----
9898

99+
=== Streaming API for Large Data
100+
101+
For processing large files without loading them entirely into memory:
102+
103+
[source,clojure]
104+
----
105+
;; Streaming compression example
106+
(require '[clojure.java.io :as io])
107+
(require '[zlib-tiny.core :as z])
108+
109+
;; Compress a large file
110+
(with-open [input (io/input-stream "large-data.txt")
111+
output (io/output-stream "large-data.gz")]
112+
(z/copy-compress input output z/gzip-stream))
113+
114+
;; Decompress a large file
115+
(with-open [input (io/input-stream "large-data.gz")
116+
output (io/output-stream "large-data-decompressed.txt")]
117+
(z/copy-decompress input output z/gunzip-stream))
118+
119+
;; Direct stream creation for custom processing
120+
(with-open [input (io/input-stream "data.txt")
121+
compressed (z/deflate-stream input)]
122+
;; Process compressed stream
123+
)
124+
----
125+
99126
==== Digests
100127

101128
[source,shell]
@@ -152,7 +179,11 @@ CRC64 checks:
152179
153180
lein test zlib-tiny.compress
154181
155-
Ran 3 tests containing 13 assertions.
182+
lein test zlib-tiny.performance
183+
...
184+
185+
Ran 4 tests containing 14 assertions.
186+
...
156187
----
157188

158189
== Manual Build
@@ -164,7 +195,7 @@ $ lein install
164195

165196
== License
166197

167-
Copyright © 2017-2023
198+
Copyright © 2017-2025
168199

169200
Distributed under the http://www.apache.org/licenses/LICENSE-2.0[Apache License v 2.0]
170201

profiles.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
:plugins []}
77

8-
:provided {:dependencies [[org.clojure/clojure "1.11.1"]]
8+
:provided {:dependencies [[org.clojure/clojure "1.12.1"]]
99
:source-paths #{"src-clj"}
1010
:java-source-paths #{"src-java"}
1111
:resource-paths ["resources"]
1212

13-
:javac-options ["-source" "9" "-target" "9" "-g:none"]
13+
:javac-options ["--release" "9" "-g:none"]
1414

1515
:jar-exclusions [#"\.java"]}
1616

project.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
(defproject net.tbt-post/zlib-tiny "0.5.2"
1+
(defproject net.tbt-post/zlib-tiny "0.6.0"
22
:description "Tiny Clojure ZLib helper"
33
:url "https://github.com/source-c/zlib-tiny"
44
:license {:name "Apache License v2.0"
55
:url "http://www.apache.org/licenses/LICENSE-2.0"}
6-
:dependencies [[commons-io "2.15.1"]])
6+
:dependencies [[commons-io "2.20.0"]])

src-clj/zlib_tiny/core.clj

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,30 @@
1616
BufferedInputStream
1717
InputStream)))
1818

19+
(def ^:private ^:const STREAM_MARK_LIMIT 512)
20+
(def ^:private ^:const DEFAULT_BUFFER_SIZE 8192)
21+
22+
(def ^:private ^ThreadLocal buffer-pool
23+
(proxy [ThreadLocal] []
24+
(initialValue []
25+
(byte-array DEFAULT_BUFFER_SIZE))))
26+
27+
(defn- get-buffer
28+
"Gets a reusable buffer from the thread-local pool"
29+
[]
30+
(.get buffer-pool))
31+
1932
(defn str->bytes
2033
"Returns the encoding's bytes corresponding to the given string. If no
2134
encoding is specified, UTF-8 is used."
2235
[^String s & [^String encoding]]
23-
(.getBytes s (or encoding "UTF-8")))
36+
(.getBytes s ^String (or encoding "UTF-8")))
2437

2538
(defn bytes->str
2639
"Returns the String corresponding to the given encoding's decoding of the
2740
given bytes. If no encoding is specified, UTF-8 is used."
2841
[^bytes b & [^String encoding]]
29-
(String. b (or encoding "UTF-8")))
42+
(String. b ^String (or encoding "UTF-8")))
3043

3144
(defn gunzip
3245
"Returns a gunzip'd version of the given byte array."
@@ -43,8 +56,14 @@
4356
[b]
4457
(when b
4558
(let [baos (ByteArrayOutputStream.)
46-
gos (GZIPOutputStream. baos)]
47-
(IOUtils/copy (ByteArrayInputStream. b) gos)
59+
gos (GZIPOutputStream. baos ^int DEFAULT_BUFFER_SIZE)
60+
buffer (get-buffer)
61+
bis (ByteArrayInputStream. b)]
62+
(loop []
63+
(let [n (.read bis buffer 0 DEFAULT_BUFFER_SIZE)]
64+
(when (pos? n)
65+
(.write gos buffer 0 n)
66+
(recur))))
4867
(.close gos)
4968
(.toByteArray baos))))
5069

@@ -66,7 +85,7 @@
6685
(let [stream (BufferedInputStream. (if (instance? InputStream b)
6786
b
6887
(ByteArrayInputStream. b)))
69-
_ (.mark stream 512)
88+
_ (.mark stream STREAM_MARK_LIMIT)
7089
iis (InflaterInputStream. stream)
7190
readable? (try (.read iis) true
7291
(catch ZipException _ false))]
@@ -138,3 +157,78 @@
138157
(defn sha-512
139158
^bytes [^bytes b]
140159
(wrap-digest "SHA-512" b))
160+
161+
;; Streaming API for large data
162+
163+
(defn deflate-stream
164+
"Returns a DeflaterInputStream for streaming deflation.
165+
Useful for large files that shouldn't be loaded entirely into memory."
166+
([^InputStream input-stream]
167+
(DeflaterInputStream. input-stream))
168+
([^InputStream input-stream level]
169+
(DeflaterInputStream. input-stream (Deflater. level))))
170+
171+
(defn inflate-stream
172+
"Returns an InflaterInputStream for streaming inflation.
173+
Useful for large files that shouldn't be loaded entirely into memory."
174+
[^InputStream input-stream]
175+
(let [stream (BufferedInputStream. input-stream)
176+
_ (.mark stream STREAM_MARK_LIMIT)
177+
iis (InflaterInputStream. stream)
178+
readable? (try (.read iis) true
179+
(catch ZipException _ false))]
180+
(.reset stream)
181+
(if readable?
182+
(InflaterInputStream. stream)
183+
(InflaterInputStream. stream (Inflater. true)))))
184+
185+
(defn gzip-stream
186+
"Returns a GZIPOutputStream for streaming gzip compression.
187+
Useful for large files that shouldn't be loaded entirely into memory."
188+
^GZIPOutputStream
189+
([^java.io.OutputStream output-stream]
190+
(GZIPOutputStream. output-stream ^int DEFAULT_BUFFER_SIZE))
191+
([^java.io.OutputStream output-stream ^Integer buffer-size]
192+
(GZIPOutputStream. output-stream ^int buffer-size)))
193+
194+
(defn gunzip-stream
195+
"Returns a GZIPInputStream for streaming gzip decompression.
196+
Useful for large files that shouldn't be loaded entirely into memory."
197+
([^InputStream input-stream]
198+
(GZIPInputStream. input-stream ^int DEFAULT_BUFFER_SIZE))
199+
([^InputStream input-stream buffer-size]
200+
(GZIPInputStream. input-stream ^int buffer-size)))
201+
202+
(defn copy-compress
203+
"Copies data from input-stream to output-stream with compression.
204+
Returns the number of bytes written."
205+
^long [^InputStream input-stream ^java.io.OutputStream output-stream compress-fn]
206+
(let [^java.io.OutputStream compressed-stream (compress-fn output-stream)
207+
^bytes buffer (get-buffer)]
208+
(try
209+
(loop [total (long 0)]
210+
(let [n (.read input-stream buffer 0 DEFAULT_BUFFER_SIZE)]
211+
(if (pos? n)
212+
(do
213+
(.write compressed-stream buffer 0 n)
214+
(recur (+ total n)))
215+
total)))
216+
(finally
217+
(.close compressed-stream)))))
218+
219+
(defn copy-decompress
220+
"Copies data from input-stream to output-stream with decompression.
221+
Returns the number of bytes written."
222+
^long [^InputStream input-stream ^java.io.OutputStream output-stream decompress-fn]
223+
(let [^InputStream decompressed-stream (decompress-fn input-stream)
224+
^bytes buffer (get-buffer)]
225+
(try
226+
(loop [total (long 0)]
227+
(let [n (.read decompressed-stream buffer 0 DEFAULT_BUFFER_SIZE)]
228+
(if (pos? n)
229+
(do
230+
(.write output-stream buffer 0 n)
231+
(recur (+ total n)))
232+
total)))
233+
(finally
234+
(.close decompressed-stream)))))

src-java/CRC32C.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,25 @@ public void update(int b) {
106106
@Override
107107
public void update(byte[] bArray, int off, int len) {
108108
long newCrc = crc ^ LONG_MASK;
109-
for (int i = off; i < off + len; i++) {
109+
int end = off + len;
110+
111+
// Process 8 bytes at a time for better performance
112+
int fastEnd = end - 7;
113+
int i = off;
114+
while (i < fastEnd) {
115+
// Process a block of 8 bytes using inner loop
116+
for (int j = 0; j < 8; j++) {
117+
newCrc = updateByte(bArray[i + j], newCrc);
118+
}
119+
i += 8;
120+
}
121+
122+
// Process remaining bytes
123+
while (i < end) {
110124
newCrc = updateByte(bArray[i], newCrc);
125+
i++;
111126
}
127+
112128
crc = newCrc ^ LONG_MASK;
113129
}
114130

src-java/CRC64.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,21 @@ public void update(byte[] buf) {
3434

3535
public void update(byte[] buf, int off, int len) {
3636
int end = off + len;
37-
38-
while (off < end)
37+
38+
// Process 8 bytes at a time for better performance
39+
int fastEnd = end - 7;
40+
while (off < fastEnd) {
41+
// Process a block of 8 bytes using inner loop
42+
for (int j = 0; j < 8; j++) {
43+
crc = crcTable[(buf[off + j] ^ (int) crc) & 0xFF] ^ (crc >>> 8);
44+
}
45+
off += 8;
46+
}
47+
48+
// Process remaining bytes
49+
while (off < end) {
3950
crc = crcTable[(buf[off++] ^ (int) crc) & 0xFF] ^ (crc >>> 8);
51+
}
4052
}
4153

4254
public long getValue() {

0 commit comments

Comments
 (0)