Skip to content

Commit 1217ef6

Browse files
committed
Added CountingReader and LimitAppendable
1 parent e0f8a8f commit 1217ef6

File tree

6 files changed

+812
-0
lines changed

6 files changed

+812
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* CountingReader.java
3+
* Copyright 2022 Rob Spoor
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.github.robtimus.obfuscation.support;
19+
20+
import java.io.IOException;
21+
import java.io.Reader;
22+
import java.util.Objects;
23+
24+
/**
25+
* A {@link Reader} that counts the number of characters that are read.
26+
*
27+
* @author Rob Spoor
28+
* @since 1.4
29+
*/
30+
public class CountingReader extends Reader {
31+
32+
private final Reader reader;
33+
34+
private long count;
35+
private long mark;
36+
37+
CountingReader(Reader reader) {
38+
this.reader = Objects.requireNonNull(reader);
39+
this.count = 0;
40+
this.mark = 0;
41+
}
42+
43+
/**
44+
* Returns the number of characters read so far. It also includes the number of skipped characters.
45+
*
46+
* @return The number of characters read or skipped so far.
47+
*/
48+
public long count() {
49+
return count;
50+
}
51+
52+
@Override
53+
public int read() throws IOException {
54+
int result = reader.read();
55+
if (result != -1) {
56+
count++;
57+
}
58+
return result;
59+
}
60+
61+
@Override
62+
public int read(char[] cbuf, int off, int len) throws IOException {
63+
int result = reader.read(cbuf, off, len);
64+
if (result != -1) {
65+
count += result;
66+
}
67+
return result;
68+
}
69+
70+
@Override
71+
public long skip(long n) throws IOException {
72+
long result = reader.skip(n);
73+
count += result;
74+
return result;
75+
}
76+
77+
@Override
78+
public boolean ready() throws IOException {
79+
return reader.ready();
80+
}
81+
82+
@Override
83+
public boolean markSupported() {
84+
return reader.markSupported();
85+
}
86+
87+
@Override
88+
public void mark(int readAheadLimit) throws IOException {
89+
reader.mark(readAheadLimit);
90+
mark = count;
91+
}
92+
93+
@Override
94+
public void reset() throws IOException {
95+
reader.reset();
96+
count = mark;
97+
}
98+
99+
@Override
100+
public void close() throws IOException {
101+
reader.close();
102+
}
103+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* LimitAppendable.java
3+
* Copyright 2022 Rob Spoor
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.github.robtimus.obfuscation.support;
19+
20+
import java.io.IOException;
21+
22+
/**
23+
* An {@link Appendable} that limits the amount of text that can be appended.
24+
*
25+
* @author Rob Spoor
26+
* @since 1.4
27+
*/
28+
public class LimitAppendable implements Appendable {
29+
30+
private final Appendable appendable;
31+
32+
private long remaining;
33+
34+
LimitAppendable(Appendable appendable, long limit) {
35+
this.appendable = appendable;
36+
this.remaining = limit;
37+
}
38+
39+
/**
40+
* Returns whether or not the limit has been reached.
41+
*
42+
* @return {@code true} if the limit has been reached, or {@code false} otherwise.
43+
*/
44+
public boolean limitReached() {
45+
return remaining <= 0;
46+
}
47+
48+
/**
49+
* Returns whether or not the limit has been exceeded. The difference between this method and {@link #limitReached()} is that this method will
50+
* return {@code false} if the total length that was appended exactly matches the limit, and {@link #limitReached()} will return {@code true}.
51+
*
52+
* @return {@code true} if the limit has been exceeded, or {@code false} otherwise.
53+
*/
54+
public boolean limitExceeded() {
55+
return remaining == -1;
56+
}
57+
58+
@Override
59+
public Appendable append(CharSequence csq) throws IOException {
60+
if (remaining > 0) {
61+
CharSequence cs = csq != null ? csq : "null"; //$NON-NLS-1$
62+
int length = cs.length();
63+
if (remaining >= length) {
64+
appendable.append(csq);
65+
remaining -= length;
66+
} else {
67+
appendable.append(csq, 0, (int) remaining);
68+
remaining = -1;
69+
}
70+
} else {
71+
remaining = -1;
72+
}
73+
return this;
74+
}
75+
76+
@Override
77+
public Appendable append(CharSequence csq, int start, int end) throws IOException {
78+
if (remaining > 0) {
79+
if (remaining >= end - start) {
80+
appendable.append(csq, start, end);
81+
remaining -= end - start;
82+
} else {
83+
appendable.append(csq, start, start + (int) remaining);
84+
remaining = -1;
85+
}
86+
} else {
87+
remaining = -1;
88+
}
89+
return this;
90+
}
91+
92+
@Override
93+
public Appendable append(char c) throws IOException {
94+
if (remaining > 0) {
95+
appendable.append(c);
96+
remaining--;
97+
} else {
98+
remaining = -1;
99+
}
100+
return this;
101+
}
102+
}

src/main/java/com/github/robtimus/obfuscation/support/ObfuscatorUtils.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,20 @@ public static Writer writer(Appendable appendable) {
320320
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
321321
}
322322

323+
/**
324+
* Returns a {@code Reader} that counts all text read from another {@code Reader}.
325+
*
326+
* @param input The {@code Reader} to read from.
327+
* @return A {@code Reader} that counts all text read from the given {@code Reader}.
328+
* @throws NullPointerException If the given {@code Reader} is {@code null}.
329+
* @since 1.4
330+
*/
331+
@SuppressWarnings("resource")
332+
public static CountingReader counting(Reader input) {
333+
Objects.requireNonNull(input);
334+
return new CountingReader(input);
335+
}
336+
323337
/**
324338
* Returns a {@code Reader} that transparently appends all text read from another {@code Reader} to an {@code Appendable}.
325339
* If the returned {@code Reader} is closed, the given {@code Reader} will be closed as well. The {@code Appendable} will not be closed though.
@@ -355,6 +369,26 @@ public static Reader readAtMost(Reader input, int limit) {
355369
return new LimitReader(input, limit);
356370
}
357371

372+
// Appendable
373+
374+
/**
375+
* Returns an {@code Appendable} that will discard text after a specific amount of text has been appended.
376+
*
377+
* @param appendable The {@code Appendable} to append to.
378+
* @param limit The maximum number of characters to append to the given {@code Appendable}.
379+
* @return An {@code Appendable} that will discard text after a specific amount of text has been appended.
380+
* @throws NullPointerException If the given {@code Appendable} is {@code null}.
381+
* @throws IllegalArgumentException If the given limit is negative.
382+
* @since 1.4
383+
*/
384+
public static LimitAppendable appendAtMost(Appendable appendable, long limit) {
385+
Objects.requireNonNull(appendable);
386+
if (limit < 0) {
387+
throw new IllegalArgumentException(limit + " < 0"); //$NON-NLS-1$
388+
}
389+
return new LimitAppendable(appendable, limit);
390+
}
391+
358392
// I/O
359393

360394
/**
@@ -449,6 +483,8 @@ private static void copyAll(Reader input, Writer destination, char[] buffer) thr
449483
}
450484
}
451485

486+
// masking
487+
452488
/**
453489
* Copies the contents of a {@code Reader} to an {@code Appendable}, masking each character.
454490
*
@@ -503,6 +539,8 @@ private static void maskAll(Reader input, Writer destination, char[] buffer, cha
503539
}
504540
}
505541

542+
// appending
543+
506544
/**
507545
* Appends a single character to an {@code Appendable}.
508546
* The character to be written is contained in the 16 low-order bits of the given integer value; the 16 high-order bits are ignored.

0 commit comments

Comments
 (0)