Skip to content

Commit

Permalink
Added FastReader and FastWriter.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdereg committed Nov 5, 2023
1 parent c5f69ec commit 57a4218
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 3 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ The classes in the`.jar`file are version 52 (`JDK 1.8`).
To include in your project:
##### Gradle
```
implementation 'com.cedarsoftware:java-util:2.2.0'
implementation 'com.cedarsoftware:java-util:2.3.0'
```

##### Maven
```
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<version>2.2.0</version>
<version>2.3.0</version>
</dependency>
```
---
Expand Down Expand Up @@ -63,6 +63,9 @@ Included in java-util:
* **Converter** - Convert from one instance to another. For example, `convert("45.3", BigDecimal.class)` will convert the `String` to a `BigDecimal`. Works for all primitives, primitive wrappers, `Date`, `java.sql.Date`, `String`, `BigDecimal`, `BigInteger`, `AtomicBoolean`, `AtomicLong`, etc. The method is very generous on what it allows to be converted. For example, a `Calendar` instance can be input for a `Date` or `Long`. Examine source to see all possibilities.
* **DateUtilities** - Robust date String parser that handles date/time, date, time, time/date, string name months or numeric months, skips comma, etc. English month names only (plus common month name abbreviations), time with/without seconds or milliseconds, `y/m/d` and `m/d/y` ordering as well.
* **DeepEquals** - Compare two object graphs and return 'true' if they are equivalent, 'false' otherwise. This will handle cycles in the graph, and will call an `equals()` method on an object if it has one, otherwise it will do a field-by-field equivalency check for non-transient fields. Has options to turn on/off using `.equals()` methods that may exist on classes.
* **IO**
* **FastReader** - Works like `BufferedReader` and `PushbackReader` without the synchronization. Tracks `line` and `col` by watching for `0x0a,` which can be useful when reading text/json/xml files. You can `.pushback()` a character read, which is very useful in parsers.
* **FastWriter** - Works like `BufferedWriter` without the synchronization.
* **EncryptionUtilities** - Makes it easy to compute MD5, SHA-1, SHA-256, SHA-512 checksums for `Strings`, `byte[]`, as well as making it easy to AES-128 encrypt `Strings` and `byte[]`'s.
* **Executor** - One line call to execute operating system commands. `Executor executor = new Executor(); executor.exec('ls -l');` Call `executor.getOut()` to fetch the output, `executor.getError()` retrieve error output. If a -1 is returned, there was an error.
* **FastByteArrayOutputStream** - Unlike the JDK `ByteArrayOutputStream`, `FastByteArrayOutputStream` is 1) not `synchronized`, and 2) allows access to it's internal `byte[]` eliminating the duplication of the `byte[]` when `toByteArray()` is called.
Expand Down
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
### Revision History
* 2.3.0
* Added `FastReader` and `FastWriter.`
* `FastReader` can be used instead of the JDK `PushbackReader(BufferedReader)).` It is much faster with no synchronization and combines both. It also tracks line `[getLine()]`and column `[getCol()]` position monitoring for `0x0a` which it can be queried for. It also can be queried for the last snippet read: `getLastSnippet().` Great for showing parsing error messages that accurately point out where a syntax error occurred. Make sure you use a new instance per each thread.
* `FastWriter` can be used instead of the JDK `BufferedWriter` as it has no synchronization. Make sure you use a new Instance per each thread.
* 2.2.0
* Built with JDK 1.8 and runs with JDK 1.8 through JDK 21.
* The 2.2.x will continue to maintain JDK 1.8. The 3.0 branch [not yet created] will be JDK11+
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<packaging>jar</packaging>
<version>2.3.0-SNAPSHOT</version>
<version>2.3.0</version>
<description>Java Utilities</description>
<url>https://github.com/jdereg/java-util</url>

Expand Down
145 changes: 145 additions & 0 deletions src/main/java/com/cedarsoftware/util/FastReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.cedarsoftware.util;

import java.io.IOException;
import java.io.Reader;

public class FastReader extends Reader {
private Reader in;
private char[] buf;
private int bufferSize;
private int pushbackBufferSize;
private int position; // Current position in the buffer
private int limit; // Number of characters currently in the buffer
private char[] pushbackBuffer;
private int pushbackPosition; // Current position in the pushback buffer
private int line = 1;
private int col = 0;

public FastReader(Reader in, int bufferSize, int pushbackBufferSize) {
super(in);
if (bufferSize <= 0 || pushbackBufferSize < 0) {
throw new IllegalArgumentException("Buffer sizes must be positive");
}
this.in = in;
this.bufferSize = bufferSize;
this.pushbackBufferSize = pushbackBufferSize;
this.buf = new char[bufferSize];
this.pushbackBuffer = new char[pushbackBufferSize];
this.position = 0;
this.limit = 0;
this.pushbackPosition = pushbackBufferSize; // Start from the end of pushbackBuffer
}

private void fill() throws IOException {
if (position >= limit) {
limit = in.read(buf, 0, bufferSize);
if (limit > 0) {
position = 0;
}
}
}

public void pushback(char ch) throws IOException {
if (pushbackPosition == 0) {
throw new IOException("Pushback buffer overflow");
}
pushbackBuffer[--pushbackPosition] = ch;
if (ch == 0x0a) {
line--;
}
else {
col--;
}
}

protected void movePosition(char ch)
{
if (ch == 0x0a) {
line++;
col = 0;
}
else {
col++;
}
}

public int read() throws IOException {
if (in == null) {
throw new IOException("FastReader stream is closed.");
}
char ch;
if (pushbackPosition < pushbackBufferSize) {
ch = pushbackBuffer[pushbackPosition++];
movePosition(ch);
return ch;
}

fill();
if (limit == -1) {
return -1;
}

ch = buf[position++];
movePosition(ch);
return ch;
}

public int read(char[] cbuf, int off, int len) throws IOException {
if (in == null) {
throw new IOException("FastReader stream is closed.");
}
int bytesRead = 0;

while (len > 0) {
int available = pushbackBufferSize - pushbackPosition;
if (available > 0) {
int toRead = Math.min(available, len);
System.arraycopy(pushbackBuffer, pushbackPosition, cbuf, off, toRead);
pushbackPosition += toRead;
off += toRead;
len -= toRead;
bytesRead += toRead;
} else {
fill();
if (limit == -1) {
return bytesRead > 0 ? bytesRead : -1;
}
int toRead = Math.min(limit - position, len);
System.arraycopy(buf, position, cbuf, off, toRead);
position += toRead;
off += toRead;
len -= toRead;
bytesRead += toRead;
}
}

return bytesRead;
}

public void close() throws IOException {
if (in != null) {
in.close();
in = null;
}
}

public int getLine()
{
return line;
}

public int getCol()
{
return col;
}

public String getLastSnippet()
{
StringBuilder s = new StringBuilder();
for (int i=0; i < position; i++)
{
s.append(buf[i]);
}
return s.toString();
}
}
102 changes: 102 additions & 0 deletions src/main/java/com/cedarsoftware/util/FastWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.cedarsoftware.util;

import java.io.IOException;
import java.io.Writer;

public class FastWriter extends Writer {
private static final int DEFAULT_BUFFER_SIZE = 8192;

private Writer out;
private char[] cb;
private int nextChar;

public FastWriter(Writer out) {
this(out, DEFAULT_BUFFER_SIZE);
}

public FastWriter(Writer out, int bufferSize) {
super(out);
if (bufferSize <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
this.out = out;
this.cb = new char[bufferSize];
this.nextChar = 0;
}

private void flushBuffer() throws IOException {
if (nextChar == 0) {
return;
}
out.write(cb, 0, nextChar);
nextChar = 0;
}

public void write(int c) throws IOException {
if (out == null) {
throw new IOException("FastWriter stream is closed.");
}
if (nextChar >= cb.length) {
flushBuffer();
}
cb[nextChar++] = (char) c;
}

public void write(char[] cbuf, int off, int len) throws IOException {
if (out == null) {
throw new IOException("FastWriter stream is closed.");
}
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
if (len >= cb.length) {
// If the request length exceeds the size of the output buffer,
// flush the buffer and then write the data directly.
flushBuffer();
out.write(cbuf, off, len);
return;
}
if (len > cb.length - nextChar) {
flushBuffer();
}
System.arraycopy(cbuf, off, cb, nextChar, len);
nextChar += len;
}

public void write(String str, int off, int len) throws IOException {
if (out == null) {
throw new IOException("FastWriter stream is closed.");
}
int b = off, t = off + len;
while (b < t) {
int d = Math.min(cb.length - nextChar, t - b);
str.getChars(b, b + d, cb, nextChar);
b += d;
nextChar += d;
if (nextChar >= cb.length) {
flushBuffer();
}
}
}

public void flush() throws IOException {
flushBuffer();
out.flush();
}

public void close() throws IOException {
if (out == null) {
return;
}
try {
flushBuffer();
} finally {
out.close();
out = null;
cb = null;
}
}
}
1 change: 1 addition & 0 deletions src/main/java/com/cedarsoftware/util/TestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,5 @@ public static boolean checkContainsIgnoreCase(String source, String... contains)
}
return true;
}

}
Loading

0 comments on commit 57a4218

Please sign in to comment.