First commit
This commit is contained in:
parent
4c94431ed6
commit
0ec246fe32
79
.github/workflows/maven-publish.yml
vendored
Normal file
79
.github/workflows/maven-publish.yml
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
|
||||||
|
# For more information see: https://github.com/actions/setup-java#apache-maven-with-a-settings-path
|
||||||
|
|
||||||
|
name: Maven Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { os: ubuntu-20.04, arch: "linux/amd64" }
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Branch name
|
||||||
|
id: branch_name
|
||||||
|
run: |
|
||||||
|
set -xeo pipefail
|
||||||
|
echo "SOURCE_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
echo "SOURCE_BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
|
||||||
|
echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
echo "SOURCE_TAG_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
cat $GITHUB_ENV > github.env
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: "recursive"
|
||||||
|
- name: Setup variables
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -xeo pipefail
|
||||||
|
# ====== Variables
|
||||||
|
export REVISION=${{ steps.branch_name.outputs.SOURCE_TAG_VERSION }}
|
||||||
|
|
||||||
|
echo "REVISION=$REVISION" >> $GITHUB_ENV
|
||||||
|
- name: Set up JDK 17 (Snapshot)
|
||||||
|
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: 17
|
||||||
|
distribution: temurin
|
||||||
|
cache: 'maven'
|
||||||
|
server-id: mchv-snapshot-distribution
|
||||||
|
server-username: MAVEN_USERNAME
|
||||||
|
server-password: MAVEN_PASSWORD
|
||||||
|
- name: Build and deploy to Maven (Snapshot)
|
||||||
|
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -xeo pipefail
|
||||||
|
|
||||||
|
mvn -B clean deploy
|
||||||
|
echo "Done."
|
||||||
|
env:
|
||||||
|
MAVEN_USERNAME: ${{ secrets.MCHV_USERNAME }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MCHV_TOKEN }}
|
||||||
|
- name: Set up JDK 17 (Release)
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: 17
|
||||||
|
distribution: temurin
|
||||||
|
cache: 'maven'
|
||||||
|
server-id: mchv-release-distribution
|
||||||
|
server-username: MAVEN_USERNAME
|
||||||
|
server-password: MAVEN_PASSWORD
|
||||||
|
- name: Build and deploy to Maven (Release)
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -xeo pipefail
|
||||||
|
echo "REVISION: $REVISION"
|
||||||
|
|
||||||
|
mvn -B clean -Drevision="${REVISION}" deploy
|
||||||
|
echo "Done."
|
||||||
|
env:
|
||||||
|
MAVEN_USERNAME: ${{ secrets.MCHV_USERNAME }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MCHV_TOKEN }}
|
98
pom.xml
98
pom.xml
@ -5,11 +5,12 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>common-utils</artifactId>
|
<groupId>it.tdlight</groupId>
|
||||||
<groupId>org.warp</groupId>
|
<artifactId>common-util</artifactId>
|
||||||
<version>1.1.8</version>
|
<version>${revision}</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
<revision>1.0.0-SNAPSHOT</revision>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@ -35,6 +36,11 @@
|
|||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jctools</groupId>
|
||||||
|
<artifactId>jctools-core</artifactId>
|
||||||
|
<version>4.0.1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
@ -47,65 +53,6 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>it.unimi.dsi</groupId>
|
|
||||||
<artifactId>fastutil</artifactId>
|
|
||||||
<version>8.5.8</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-lang3</artifactId>
|
|
||||||
<version>3.12.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jetbrains</groupId>
|
|
||||||
<artifactId>annotations</artifactId>
|
|
||||||
<version>23.0.0</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>31.1-jre</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.squareup.moshi</groupId>
|
|
||||||
<artifactId>moshi</artifactId>
|
|
||||||
<version>1.13.0</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
|
||||||
<artifactId>kotlin-stdlib</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
|
||||||
<artifactId>kotlin-stdlib-common</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>dev.zacsweers.moshix</groupId>
|
|
||||||
<artifactId>moshi-records-reflect</artifactId>
|
|
||||||
<version>0.14.1</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.squareup.moshi</groupId>
|
|
||||||
<artifactId>moshi</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jetbrains.kotlin</groupId>
|
|
||||||
<!--suppress KotlinMavenPluginPhase -->
|
|
||||||
<artifactId>kotlin-stdlib</artifactId>
|
|
||||||
<version>1.6.0</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jetbrains</groupId>
|
|
||||||
<artifactId>annotations</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
@ -115,7 +62,7 @@
|
|||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<encoding>UTF-8</encoding>
|
<encoding>UTF-8</encoding>
|
||||||
<release>11</release>
|
<release>8</release>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@ -136,6 +83,31 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>flatten-maven-plugin</artifactId>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<updatePomFile>true</updatePomFile>
|
||||||
|
<flattenMode>oss</flattenMode>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>flatten</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>flatten</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>flatten.clean</id>
|
||||||
|
<phase>clean</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>clean</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
@ -1,229 +0,0 @@
|
|||||||
/*BEGIN_COPYRIGHT_BLOCK
|
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* This software is Open Source Initiative approved Open Source Software.
|
|
||||||
* Open Source Initative Approved is a trademark of the Open Source Initiative.
|
|
||||||
*
|
|
||||||
* This file is part of DrJava. Download the current version of this project
|
|
||||||
* from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
|
|
||||||
*
|
|
||||||
* END_COPYRIGHT_BLOCK*/
|
|
||||||
|
|
||||||
package edu.rice.cs.util;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class which can tokenize a String into a list of String arguments,
|
|
||||||
* with behavior similar to parsing command line arguments to a program.
|
|
||||||
* Quoted Strings are treated as single arguments, and escaped characters
|
|
||||||
* are translated so that the tokenized arguments have the same meaning.
|
|
||||||
* Since all methods are static, the class is declared abstract to prevent
|
|
||||||
* instantiation.
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
|
||||||
public abstract class ArgumentTokenizer {
|
|
||||||
private static final int NO_TOKEN_STATE = 0;
|
|
||||||
private static final int NORMAL_TOKEN_STATE = 1;
|
|
||||||
private static final int SINGLE_QUOTE_STATE = 2;
|
|
||||||
private static final int DOUBLE_QUOTE_STATE = 3;
|
|
||||||
|
|
||||||
/** Tokenizes the given String into String tokens
|
|
||||||
* @param arguments A String containing one or more command-line style arguments to be tokenized.
|
|
||||||
* @return A list of parsed and properly escaped arguments.
|
|
||||||
*/
|
|
||||||
public static List<String> tokenize(String arguments) {
|
|
||||||
return tokenize(arguments, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tokenizes the given String into String tokens.
|
|
||||||
* @param arguments A String containing one or more command-line style arguments to be tokenized.
|
|
||||||
* @param stringify whether or not to include escape special characters
|
|
||||||
* @return A list of parsed and properly escaped arguments.
|
|
||||||
*/
|
|
||||||
public static List<String> tokenize(String arguments, boolean stringify) {
|
|
||||||
|
|
||||||
LinkedList<String> argList = new LinkedList<String>();
|
|
||||||
StringBuilder currArg = new StringBuilder();
|
|
||||||
boolean escaped = false;
|
|
||||||
int state = NO_TOKEN_STATE; // start in the NO_TOKEN_STATE
|
|
||||||
int len = arguments.length();
|
|
||||||
|
|
||||||
// Loop over each character in the string
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
char c = arguments.charAt(i);
|
|
||||||
if (escaped) {
|
|
||||||
// Escaped state: just append the next character to the current arg.
|
|
||||||
escaped = false;
|
|
||||||
currArg.append(c);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch(state) {
|
|
||||||
case SINGLE_QUOTE_STATE:
|
|
||||||
if (c == '\'') {
|
|
||||||
// Seen the close quote; continue this arg until whitespace is seen
|
|
||||||
state = NORMAL_TOKEN_STATE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currArg.append(c);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DOUBLE_QUOTE_STATE:
|
|
||||||
if (c == '"') {
|
|
||||||
// Seen the close quote; continue this arg until whitespace is seen
|
|
||||||
state = NORMAL_TOKEN_STATE;
|
|
||||||
}
|
|
||||||
else if (c == '\\') {
|
|
||||||
// Look ahead, and only escape quotes or backslashes
|
|
||||||
i++;
|
|
||||||
char next = arguments.charAt(i);
|
|
||||||
if (next == '"' || next == '\\') {
|
|
||||||
currArg.append(next);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currArg.append(c);
|
|
||||||
currArg.append(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currArg.append(c);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// case NORMAL_TOKEN_STATE:
|
|
||||||
// if (Character.isWhitespace(c)) {
|
|
||||||
// // Whitespace ends the token; start a new one
|
|
||||||
// argList.add(currArg.toString());
|
|
||||||
// currArg = new StringBuffer();
|
|
||||||
// state = NO_TOKEN_STATE;
|
|
||||||
// }
|
|
||||||
// else if (c == '\\') {
|
|
||||||
// // Backslash in a normal token: escape the next character
|
|
||||||
// escaped = true;
|
|
||||||
// }
|
|
||||||
// else if (c == '\'') {
|
|
||||||
// state = SINGLE_QUOTE_STATE;
|
|
||||||
// }
|
|
||||||
// else if (c == '"') {
|
|
||||||
// state = DOUBLE_QUOTE_STATE;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// currArg.append(c);
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
case NO_TOKEN_STATE:
|
|
||||||
case NORMAL_TOKEN_STATE:
|
|
||||||
switch(c) {
|
|
||||||
case '\\':
|
|
||||||
escaped = true;
|
|
||||||
state = NORMAL_TOKEN_STATE;
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
state = SINGLE_QUOTE_STATE;
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
state = DOUBLE_QUOTE_STATE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!Character.isWhitespace(c)) {
|
|
||||||
currArg.append(c);
|
|
||||||
state = NORMAL_TOKEN_STATE;
|
|
||||||
}
|
|
||||||
else if (state == NORMAL_TOKEN_STATE) {
|
|
||||||
// Whitespace ends the token; start a new one
|
|
||||||
argList.add(currArg.toString());
|
|
||||||
currArg = new StringBuilder();
|
|
||||||
state = NO_TOKEN_STATE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("ArgumentTokenizer state " + state + " is invalid!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're still escaped, put in the backslash
|
|
||||||
if (escaped) {
|
|
||||||
currArg.append('\\');
|
|
||||||
argList.add(currArg.toString());
|
|
||||||
}
|
|
||||||
// Close the last argument if we haven't yet
|
|
||||||
else if (state != NO_TOKEN_STATE) {
|
|
||||||
argList.add(currArg.toString());
|
|
||||||
}
|
|
||||||
// Format each argument if we've been told to stringify them
|
|
||||||
if (stringify) {
|
|
||||||
for (int i = 0; i < argList.size(); i++) {
|
|
||||||
argList.set(i, "\"" + _escapeQuotesAndBackslashes(argList.get(i)) + "\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return argList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Inserts backslashes before any occurrences of a backslash or
|
|
||||||
* quote in the given string. Also converts any special characters
|
|
||||||
* appropriately.
|
|
||||||
*/
|
|
||||||
protected static String _escapeQuotesAndBackslashes(String s) {
|
|
||||||
final StringBuilder buf = new StringBuilder(s);
|
|
||||||
|
|
||||||
// Walk backwards, looking for quotes or backslashes.
|
|
||||||
// If we see any, insert an extra backslash into the buffer at
|
|
||||||
// the same index. (By walking backwards, the index into the buffer
|
|
||||||
// will remain correct as we change the buffer.)
|
|
||||||
for (int i = s.length()-1; i >= 0; i--) {
|
|
||||||
char c = s.charAt(i);
|
|
||||||
if ((c == '\\') || (c == '"')) {
|
|
||||||
buf.insert(i, '\\');
|
|
||||||
}
|
|
||||||
// Replace any special characters with escaped versions
|
|
||||||
else if (c == '\n') {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
buf.insert(i, "\\n");
|
|
||||||
}
|
|
||||||
else if (c == '\t') {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
buf.insert(i, "\\t");
|
|
||||||
}
|
|
||||||
else if (c == '\r') {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
buf.insert(i, "\\r");
|
|
||||||
}
|
|
||||||
else if (c == '\b') {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
buf.insert(i, "\\b");
|
|
||||||
}
|
|
||||||
else if (c == '\f') {
|
|
||||||
buf.deleteCharAt(i);
|
|
||||||
buf.insert(i, "\\f");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
|
25
src/main/java/it/tdlight/commonutil/IntIterator.java
Normal file
25
src/main/java/it/tdlight/commonutil/IntIterator.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package it.tdlight.commonutil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An iterator optimized for primitive collections which avoids auto-boxing on {@link #next()}.
|
||||||
|
*/
|
||||||
|
public interface IntIterator {
|
||||||
|
/**
|
||||||
|
* Identical to {@link java.util.Iterator#next()} but avoids auto-boxing.
|
||||||
|
*
|
||||||
|
* @return The next int in the collection.
|
||||||
|
*/
|
||||||
|
int next();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identical to {@link java.util.Iterator#hasNext()}.
|
||||||
|
*
|
||||||
|
* @return True if the iterator has more elements.
|
||||||
|
*/
|
||||||
|
boolean hasNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identical to {@link java.util.Iterator#remove()}.
|
||||||
|
*/
|
||||||
|
void remove();
|
||||||
|
}
|
16
src/main/java/it/tdlight/commonutil/LongIterator.java
Normal file
16
src/main/java/it/tdlight/commonutil/LongIterator.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package it.tdlight.commonutil;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of the standard {@link Iterator} interface which provides the {@link #nextLong()} method to avoid
|
||||||
|
* auto-boxing of results as they are returned.
|
||||||
|
* */
|
||||||
|
public interface LongIterator extends Iterator<Long> {
|
||||||
|
/**
|
||||||
|
* Returns the next long value without auto-boxing. Using this is preferred to {@link #next()}.
|
||||||
|
*
|
||||||
|
* @return The next long value.
|
||||||
|
*/
|
||||||
|
long nextLong();
|
||||||
|
}
|
188
src/main/java/it/tdlight/commonutil/NonBlockingHashSetLong.java
Normal file
188
src/main/java/it/tdlight/commonutil/NonBlockingHashSetLong.java
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package it.tdlight.commonutil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.jctools.maps.NonBlockingHashMapLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple wrapper around {@link NonBlockingHashMapLong} making it implement the
|
||||||
|
* {@link Set} interface. All operations are Non-Blocking and multi-thread safe.
|
||||||
|
*/
|
||||||
|
public class NonBlockingHashSetLong extends AbstractSet<Long> implements Serializable {
|
||||||
|
private static final Object V = "";
|
||||||
|
|
||||||
|
private final NonBlockingHashMapLong<Object> _map;
|
||||||
|
|
||||||
|
/** Make a new empty {@link NonBlockingHashSetLong}. */
|
||||||
|
public NonBlockingHashSetLong() {
|
||||||
|
super();
|
||||||
|
_map = new NonBlockingHashMapLong<Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends Long> c) {
|
||||||
|
if (!NonBlockingHashSetLong.class.equals(c.getClass())) {
|
||||||
|
return super.addAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
for (final LongIterator it = ((NonBlockingHashSetLong)c).longIterator(); it.hasNext(); ) {
|
||||||
|
modified |= add(it.nextLong());
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingHashSetLong.class.equals(c.getClass())) {
|
||||||
|
return super.removeAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
for (final LongIterator it = ((NonBlockingHashSetLong)c).longIterator(); it.hasNext(); ) {
|
||||||
|
modified |= remove(it.nextLong());
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingHashSetLong.class.equals(c.getClass())) {
|
||||||
|
return super.containsAll(c);
|
||||||
|
}
|
||||||
|
for (final LongIterator it = ((NonBlockingHashSetLong)c).longIterator(); it.hasNext(); ) {
|
||||||
|
if (!contains(it.nextLong())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingHashSetLong.class.equals(c.getClass())) {
|
||||||
|
return super.retainAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
final NonBlockingHashSetLong nonBlockingHashSetLong = (NonBlockingHashSetLong) c;
|
||||||
|
for (final LongIterator it = longIterator(); it.hasNext(); ) {
|
||||||
|
if (!nonBlockingHashSetLong.contains(it.nextLong())) {
|
||||||
|
it.remove();
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hashCode = 0;
|
||||||
|
for (final LongIterator it = longIterator(); it.hasNext(); ) {
|
||||||
|
final long value = it.nextLong();
|
||||||
|
hashCode += (int)(value ^ (value >>> 32));
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add {@code o} to the set.
|
||||||
|
* @return <tt>true</tt> if {@code o} was added to the set, <tt>false</tt>
|
||||||
|
* if {@code o} was already in the set.
|
||||||
|
*/
|
||||||
|
public boolean add(final long o) {
|
||||||
|
return _map.putIfAbsent(o,V) != V;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To support AbstractCollection.addAll
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean add(final Long o) {
|
||||||
|
return _map.putIfAbsent(o.longValue(),V) != V;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <tt>true</tt> if {@code o} is in the set.
|
||||||
|
*/
|
||||||
|
public boolean contains(final long o) { return _map.containsKey(o); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return o instanceof Long && contains(((Long) o).longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove {@code o} from the set.
|
||||||
|
* @return <tt>true</tt> if {@code o} was removed to the set, <tt>false</tt>
|
||||||
|
* if {@code o} was not in the set.
|
||||||
|
*/
|
||||||
|
public boolean remove(final long o) { return _map.remove(o) == V; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(final Object o) { return o instanceof Long && remove(((Long) o).longValue()); }
|
||||||
|
/**
|
||||||
|
* Current count of elements in the set. Due to concurrent racing updates,
|
||||||
|
* the size is only ever approximate. Updates due to the calling thread are
|
||||||
|
* immediately visible to calling thread.
|
||||||
|
* @return count of elements.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int size() { return _map.size(); }
|
||||||
|
/** Empty the set. */
|
||||||
|
@Override
|
||||||
|
public void clear() { _map.clear(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// Overloaded to avoid auto-boxing
|
||||||
|
final LongIterator it = longIterator();
|
||||||
|
if (!it.hasNext()) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
final StringBuilder sb = new StringBuilder().append('[');
|
||||||
|
for (;;) {
|
||||||
|
sb.append(it.next());
|
||||||
|
if (!it.hasNext()) {
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Long>iterator() { return _map.keySet().iterator(); }
|
||||||
|
|
||||||
|
public LongIterator longIterator() {
|
||||||
|
return (LongIterator) _map.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically make the set immutable. Future calls to mutate will throw an
|
||||||
|
* IllegalStateException. Existing mutator calls in other threads racing
|
||||||
|
* with this thread and will either throw IllegalStateException or their
|
||||||
|
* update will be visible to this thread. This implies that a simple flag
|
||||||
|
* cannot make the Set immutable, because a late-arriving update in another
|
||||||
|
* thread might see immutable flag not set yet, then mutate the Set after
|
||||||
|
* the {@link #readOnly} call returns. This call can be called concurrently
|
||||||
|
* (and indeed until the operation completes, all calls on the Set from any
|
||||||
|
* thread either complete normally or end up calling {@link #readOnly}
|
||||||
|
* internally).
|
||||||
|
*
|
||||||
|
* <p> This call is useful in debugging multi-threaded programs where the
|
||||||
|
* Set is constructed in parallel, but construction completes after some
|
||||||
|
* time; and after construction the Set is only read. Making the Set
|
||||||
|
* read-only will cause updates arriving after construction is supposedly
|
||||||
|
* complete to throw an {@link IllegalStateException}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// (1) call _map's immutable() call
|
||||||
|
// (2) get snapshot
|
||||||
|
// (3) CAS down a local map, power-of-2 larger than _map.size()+1/8th
|
||||||
|
// (4) start @ random, visit all snapshot, insert live keys
|
||||||
|
// (5) CAS _map to null, needs happens-after (4)
|
||||||
|
// (6) if Set call sees _map is null, needs happens-after (4) for readers
|
||||||
|
public void readOnly() {
|
||||||
|
throw new RuntimeException("Unimplemented");
|
||||||
|
}
|
||||||
|
}
|
725
src/main/java/it/tdlight/commonutil/NonBlockingSetInt.java
Normal file
725
src/main/java/it/tdlight/commonutil/NonBlockingSetInt.java
Normal file
@ -0,0 +1,725 @@
|
|||||||
|
/*
|
||||||
|
* Written by Cliff Click and released to the public domain, as explained at
|
||||||
|
* http://creativecommons.org/licenses/publicdomain
|
||||||
|
*/
|
||||||
|
|
||||||
|
package it.tdlight.commonutil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import org.jctools.maps.ConcurrentAutoTable;
|
||||||
|
import org.jctools.util.UnsafeAccess;
|
||||||
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A multi-threaded bit-vector set, implemented as an array of primitive
|
||||||
|
* {@code longs}. All operations are non-blocking and multi-threaded safe.
|
||||||
|
* {@link #contains(int)} calls are roughly the same speed as a {load, mask}
|
||||||
|
* sequence. {@link #add(int)} and {@link #remove(int)} calls are a tad more
|
||||||
|
* expensive than a {load, mask, store} sequence because they must use a CAS.
|
||||||
|
* The bit-vector is auto-sizing.
|
||||||
|
*
|
||||||
|
* <p><em>General note of caution:</em> The Set API allows the use of {@link Integer}
|
||||||
|
* with silent autoboxing - which can be very expensive if many calls are
|
||||||
|
* being made. Since autoboxing is silent you may not be aware that this is
|
||||||
|
* going on. The built-in API takes lower-case {@code ints} and is much more
|
||||||
|
* efficient.
|
||||||
|
*
|
||||||
|
* <p>Space: space is used in proportion to the largest element, as opposed to
|
||||||
|
* the number of elements (as is the case with hash-table based Set
|
||||||
|
* implementations). Space is approximately (largest_element/8 + 64) bytes.
|
||||||
|
*
|
||||||
|
* The implementation is a simple bit-vector using CAS for update.
|
||||||
|
*
|
||||||
|
* @since 1.5
|
||||||
|
* @author Cliff Click
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class NonBlockingSetInt extends AbstractSet<Integer> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1234123412341234123L;
|
||||||
|
private static final Unsafe _unsafe = UnsafeAccess.UNSAFE;
|
||||||
|
|
||||||
|
// --- Bits to allow atomic update of the NBSI
|
||||||
|
private static final long _nbsi_offset;
|
||||||
|
static { // <clinit>
|
||||||
|
Field f = null;
|
||||||
|
try {
|
||||||
|
f = NonBlockingSetInt.class.getDeclaredField("_nbsi");
|
||||||
|
} catch( NoSuchFieldException e ) {
|
||||||
|
}
|
||||||
|
_nbsi_offset = _unsafe.objectFieldOffset(f);
|
||||||
|
}
|
||||||
|
private final boolean CAS_nbsi( NBSI old, NBSI nnn ) {
|
||||||
|
return _unsafe.compareAndSwapObject(this, _nbsi_offset, old, nnn );
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual Set of Joy, which changes during a resize event. The
|
||||||
|
// Only Field for this class, so I can atomically change the entire
|
||||||
|
// set implementation with a single CAS.
|
||||||
|
private transient NBSI _nbsi;
|
||||||
|
|
||||||
|
/** Create a new empty bit-vector */
|
||||||
|
public NonBlockingSetInt( ) {
|
||||||
|
_nbsi = new NBSI(63, new ConcurrentAutoTable(), this); // The initial 1-word set
|
||||||
|
}
|
||||||
|
|
||||||
|
private NonBlockingSetInt(NonBlockingSetInt a, NonBlockingSetInt b) {
|
||||||
|
_nbsi = new NBSI(a._nbsi,b._nbsi,new ConcurrentAutoTable(),this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to avoid auto-boxing for NonBlockingSetInt.
|
||||||
|
*
|
||||||
|
* @param c The collection to add to this set.
|
||||||
|
* @return True if the set was modified.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends Integer> c) {
|
||||||
|
if (!NonBlockingSetInt.class.equals(c.getClass())) {
|
||||||
|
return super.addAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
for (final IntIterator it = ((NonBlockingSetInt)c).intIterator(); it.hasNext(); ) {
|
||||||
|
modified |= add(it.next());
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to avoid auto-boxing for NonBlockingSetInt.
|
||||||
|
*
|
||||||
|
* @param c The collection to remove from this set.
|
||||||
|
* @return True if the set was modified.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingSetInt.class.equals(c.getClass())) {
|
||||||
|
return super.removeAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
for (final IntIterator it = ((NonBlockingSetInt)c).intIterator(); it.hasNext(); ) {
|
||||||
|
modified |= remove(it.next());
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingSetInt.class.equals(c.getClass())) {
|
||||||
|
return super.containsAll(c);
|
||||||
|
}
|
||||||
|
for (final IntIterator it = ((NonBlockingSetInt)c).intIterator(); it.hasNext(); ) {
|
||||||
|
if (!contains(it.next())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
if (!NonBlockingSetInt.class.equals(c.getClass())) {
|
||||||
|
return super.retainAll(c);
|
||||||
|
}
|
||||||
|
boolean modified = false;
|
||||||
|
final NonBlockingSetInt nonBlockingSetInt = (NonBlockingSetInt) c;
|
||||||
|
for (final IntIterator it = intIterator(); it.hasNext(); ) {
|
||||||
|
if (!nonBlockingSetInt.contains(it.next())) {
|
||||||
|
it.remove();
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hashCode = 0;
|
||||||
|
for (final IntIterator it = intIterator(); it.hasNext(); ) {
|
||||||
|
hashCode += it.next();
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add {@code i} to the set. Uppercase {@link Integer} version of add,
|
||||||
|
* requires auto-unboxing. When possible use the {@code int} version of
|
||||||
|
* {@link #add(int)} for efficiency.
|
||||||
|
* @throws IllegalArgumentException if i is negative.
|
||||||
|
* @return <tt>true</tt> if i was added to the set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean add ( final Integer i ) {
|
||||||
|
return add(i.intValue());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Test if {@code o} is in the set. This is the uppercase {@link Integer}
|
||||||
|
* version of contains, requires a type-check and auto-unboxing. When
|
||||||
|
* possible use the {@code int} version of {@link #contains(int)} for
|
||||||
|
* efficiency.
|
||||||
|
* @return <tt>true</tt> if i was in the set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean contains( final Object o ) {
|
||||||
|
return o instanceof Integer && contains(((Integer) o).intValue());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Remove {@code o} from the set. This is the uppercase {@link Integer}
|
||||||
|
* version of remove, requires a type-check and auto-unboxing. When
|
||||||
|
* possible use the {@code int} version of {@link #remove(int)} for
|
||||||
|
* efficiency.
|
||||||
|
* @return <tt>true</tt> if i was removed to the set.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean remove( final Object o ) {
|
||||||
|
return o instanceof Integer && remove(((Integer) o).intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add {@code i} to the set. This is the lower-case '{@code int}' version
|
||||||
|
* of {@link #add} - no autoboxing. Negative values throw
|
||||||
|
* IllegalArgumentException.
|
||||||
|
* @throws IllegalArgumentException if i is negative.
|
||||||
|
* @return <tt>true</tt> if i was added to the set.
|
||||||
|
*/
|
||||||
|
public boolean add( final int i ) {
|
||||||
|
if( i < 0 ) throw new IllegalArgumentException(""+i);
|
||||||
|
return _nbsi.add(i);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Test if {@code i} is in the set. This is the lower-case '{@code int}'
|
||||||
|
* version of {@link #contains} - no autoboxing.
|
||||||
|
* @return <tt>true</tt> if i was int the set.
|
||||||
|
*/
|
||||||
|
public boolean contains( final int i ) { return i >= 0 && _nbsi.contains(i); }
|
||||||
|
/**
|
||||||
|
* Remove {@code i} from the set. This is the fast lower-case '{@code int}'
|
||||||
|
* version of {@link #remove} - no autoboxing.
|
||||||
|
* @return <tt>true</tt> if i was added to the set.
|
||||||
|
*/
|
||||||
|
public boolean remove ( final int i ) { return i >= 0 && _nbsi.remove(i); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current count of elements in the set. Due to concurrent racing updates,
|
||||||
|
* the size is only ever approximate. Updates due to the calling thread are
|
||||||
|
* immediately visible to calling thread.
|
||||||
|
* @return count of elements.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int size ( ) { return _nbsi.size( ); }
|
||||||
|
/** Empty the bitvector. */
|
||||||
|
@Override
|
||||||
|
public void clear ( ) {
|
||||||
|
NBSI cleared = new NBSI(63, new ConcurrentAutoTable(), this); // An empty initial NBSI
|
||||||
|
while( !CAS_nbsi( _nbsi, cleared ) ) // Spin until clear works
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// Overloaded to avoid auto-boxing
|
||||||
|
final IntIterator it = intIterator();
|
||||||
|
if (!it.hasNext()) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
final StringBuilder sb = new StringBuilder().append('[');
|
||||||
|
for (;;) {
|
||||||
|
sb.append(it.next());
|
||||||
|
if (!it.hasNext()) {
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sizeInBytes() { return _nbsi.sizeInBytes(); }
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
*
|
||||||
|
* bitwise comparisons optimised for NBSI
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
public NonBlockingSetInt intersect(final NonBlockingSetInt op) {
|
||||||
|
NonBlockingSetInt res = new NonBlockingSetInt(this,op);
|
||||||
|
res._nbsi.intersect(res._nbsi, this._nbsi, op._nbsi);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NonBlockingSetInt union(final NonBlockingSetInt op) {
|
||||||
|
NonBlockingSetInt res = new NonBlockingSetInt(this,op);
|
||||||
|
res._nbsi.union(res._nbsi, this._nbsi, op._nbsi);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public NonBlockingSetInt not(final NonBlockingSetInt op) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** Verbose printout of internal structure for debugging. */
|
||||||
|
public void print() { _nbsi.print(0); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard Java {@link Iterator}. Not very efficient because it
|
||||||
|
* auto-boxes the returned values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Integer> iterator( ) { return new iter(); }
|
||||||
|
|
||||||
|
public IntIterator intIterator() { return new NBSIIntIterator(); }
|
||||||
|
|
||||||
|
private class NBSIIntIterator implements IntIterator {
|
||||||
|
|
||||||
|
NBSI nbsi;
|
||||||
|
int index = -1;
|
||||||
|
int prev = -1;
|
||||||
|
|
||||||
|
NBSIIntIterator() {
|
||||||
|
nbsi = _nbsi;
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advance() {
|
||||||
|
while( true ) {
|
||||||
|
index++; // Next index
|
||||||
|
while( (index>>6) >= nbsi._bits.length ) { // Index out of range?
|
||||||
|
if( nbsi._new == null ) { // New table?
|
||||||
|
index = -2; // No, so must be all done
|
||||||
|
return; //
|
||||||
|
}
|
||||||
|
nbsi = nbsi._new; // Carry on, in the new table
|
||||||
|
}
|
||||||
|
if( nbsi.contains(index) ) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int next() {
|
||||||
|
if( index == -1 ) throw new NoSuchElementException();
|
||||||
|
prev = index;
|
||||||
|
advance();
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return index != -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
if( prev == -1 ) throw new IllegalStateException();
|
||||||
|
nbsi.remove(prev);
|
||||||
|
prev = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class iter implements Iterator<Integer> {
|
||||||
|
NBSIIntIterator intIterator;
|
||||||
|
iter() { intIterator = new NBSIIntIterator(); }
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() { return intIterator.hasNext(); }
|
||||||
|
@Override
|
||||||
|
public Integer next() { return intIterator.next(); }
|
||||||
|
@Override
|
||||||
|
public void remove() { intIterator.remove(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- writeObject -------------------------------------------------------
|
||||||
|
// Write a NBSI to a stream
|
||||||
|
private void writeObject(java.io.ObjectOutputStream s) throws IOException {
|
||||||
|
s.defaultWriteObject(); // Nothing to write
|
||||||
|
final NBSI nbsi = _nbsi; // The One Field is transient
|
||||||
|
final int len = _nbsi._bits.length<<6;
|
||||||
|
s.writeInt(len); // Write max element
|
||||||
|
for( int i=0; i<len; i++ )
|
||||||
|
s.writeBoolean( _nbsi.contains(i) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- readObject --------------------------------------------------------
|
||||||
|
// Read a CHM from a stream
|
||||||
|
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
|
||||||
|
s.defaultReadObject(); // Read nothing
|
||||||
|
final int len = s.readInt(); // Read max element
|
||||||
|
_nbsi = new NBSI(len, new ConcurrentAutoTable(), this);
|
||||||
|
for( int i=0; i<len; i++ ) // Read all bits
|
||||||
|
if( s.readBoolean() )
|
||||||
|
_nbsi.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NBSI ----------------------------------------------------------------
|
||||||
|
private static final class NBSI {
|
||||||
|
// Back pointer to the parent wrapper; sorta like make the class non-static
|
||||||
|
private transient final NonBlockingSetInt _non_blocking_set_int;
|
||||||
|
|
||||||
|
// Used to count elements: a high-performance counter.
|
||||||
|
private transient final ConcurrentAutoTable _size;
|
||||||
|
|
||||||
|
// The Bits
|
||||||
|
private final long _bits[];
|
||||||
|
// --- Bits to allow Unsafe access to arrays
|
||||||
|
private static final int _Lbase = _unsafe.arrayBaseOffset(long[].class);
|
||||||
|
private static final int _Lscale = _unsafe.arrayIndexScale(long[].class);
|
||||||
|
private static long rawIndex(final long[] ary, final int idx) {
|
||||||
|
assert idx >= 0 && idx < ary.length;
|
||||||
|
return _Lbase + idx * _Lscale;
|
||||||
|
}
|
||||||
|
private final boolean CAS( int idx, long old, long nnn ) {
|
||||||
|
return _unsafe.compareAndSwapLong( _bits, rawIndex(_bits, idx), old, nnn );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Resize
|
||||||
|
// The New Table, only set once to non-zero during a resize.
|
||||||
|
// Must be atomically set.
|
||||||
|
private NBSI _new;
|
||||||
|
private static final long _new_offset;
|
||||||
|
static { // <clinit>
|
||||||
|
Field f = null;
|
||||||
|
try {
|
||||||
|
f = NBSI.class.getDeclaredField("_new");
|
||||||
|
} catch( NoSuchFieldException e ) {
|
||||||
|
}
|
||||||
|
_new_offset = _unsafe.objectFieldOffset(f);
|
||||||
|
}
|
||||||
|
private final boolean CAS_new( NBSI nnn ) {
|
||||||
|
return _unsafe.compareAndSwapObject(this, _new_offset, null, nnn );
|
||||||
|
}
|
||||||
|
|
||||||
|
private transient final AtomicInteger _copyIdx; // Used to count bits started copying
|
||||||
|
private transient final AtomicInteger _copyDone; // Used to count words copied in a resize operation
|
||||||
|
private transient final int _sum_bits_length; // Sum of all nested _bits.lengths
|
||||||
|
|
||||||
|
private static final long mask( int i ) { return 1L<<(i&63); }
|
||||||
|
|
||||||
|
// I need 1 free bit out of 64 to allow for resize. I do this by stealing
|
||||||
|
// the high order bit - but then I need to do something with adding element
|
||||||
|
// number 63 (and friends). I could use a mod63 function but it's more
|
||||||
|
// efficient to handle the mod-64 case as an exception.
|
||||||
|
//
|
||||||
|
// Every 64th bit is put in it's own recursive bitvector. If the low 6 bits
|
||||||
|
// are all set, we shift them off and recursively operate on the _nbsi64 set.
|
||||||
|
private final NBSI _nbsi64;
|
||||||
|
|
||||||
|
private NBSI(int max_elem, ConcurrentAutoTable ctr, NonBlockingSetInt nonb ) {
|
||||||
|
super();
|
||||||
|
_non_blocking_set_int = nonb;
|
||||||
|
_size = ctr;
|
||||||
|
_copyIdx = ctr == null ? null : new AtomicInteger();
|
||||||
|
_copyDone = ctr == null ? null : new AtomicInteger();
|
||||||
|
// The main array of bits
|
||||||
|
_bits = new long[(int)(((long)max_elem+63)>>>6)];
|
||||||
|
// Every 64th bit is moved off to it's own subarray, so that the
|
||||||
|
// sign-bit is free for other purposes
|
||||||
|
_nbsi64 = ((max_elem+1)>>>6) == 0 ? null : new NBSI((max_elem+1)>>>6, null, null);
|
||||||
|
_sum_bits_length = _bits.length + (_nbsi64==null ? 0 : _nbsi64._sum_bits_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** built a new NBSI with buffers large enough to hold bitwise operations on the operands **/
|
||||||
|
private NBSI(NBSI a, NBSI b, ConcurrentAutoTable ctr, NonBlockingSetInt nonb) {
|
||||||
|
super();
|
||||||
|
_non_blocking_set_int = nonb;
|
||||||
|
_size = ctr;
|
||||||
|
_copyIdx = ctr == null ? null : new AtomicInteger();
|
||||||
|
_copyDone = ctr == null ? null : new AtomicInteger();
|
||||||
|
|
||||||
|
if(!has_bits(a) && !has_bits(b)) {
|
||||||
|
_bits = null;
|
||||||
|
_nbsi64 = null;
|
||||||
|
_sum_bits_length = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - clean this nastiness up
|
||||||
|
// essentially just safely creates new empty buffers for each of the recursive bitsets
|
||||||
|
if(!has_bits(a)) {
|
||||||
|
_bits = new long[b._bits.length];
|
||||||
|
_nbsi64 = new NBSI(null,b._nbsi64,null,null);
|
||||||
|
} else if(!has_bits(b)) {
|
||||||
|
_bits = new long[a._bits.length];
|
||||||
|
_nbsi64 = new NBSI(null,a._nbsi64,null,null);
|
||||||
|
} else {
|
||||||
|
int bit_length = a._bits.length > b._bits.length ? a._bits.length : b._bits.length;
|
||||||
|
_bits = new long[bit_length];
|
||||||
|
_nbsi64 = new NBSI(a._nbsi64,b._nbsi64,null,null);
|
||||||
|
}
|
||||||
|
_sum_bits_length = _bits.length + _nbsi64._sum_bits_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean has_bits(NBSI n) {
|
||||||
|
return n != null && n._bits != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower-case 'int' versions - no autoboxing, very fast.
|
||||||
|
// 'i' is known positive.
|
||||||
|
public boolean add( final int i ) {
|
||||||
|
// Check for out-of-range for the current size bit vector.
|
||||||
|
// If so we need to grow the bit vector.
|
||||||
|
if( (i>>6) >= _bits.length )
|
||||||
|
return install_larger_new_bits(i). // Install larger pile-o-bits (duh)
|
||||||
|
help_copy().add(i); // Finally, add to the new table
|
||||||
|
|
||||||
|
// Handle every 64th bit via using a nested array
|
||||||
|
NBSI nbsi = this; // The bit array being added into
|
||||||
|
int j = i; // The bit index being added
|
||||||
|
while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set)
|
||||||
|
nbsi = nbsi._nbsi64; // Recurse
|
||||||
|
j = j>>6; // Strip off low 6 bits (all set)
|
||||||
|
}
|
||||||
|
|
||||||
|
final long mask = mask(j);
|
||||||
|
long old;
|
||||||
|
do {
|
||||||
|
old = nbsi._bits[j>>6]; // Read old bits
|
||||||
|
if( old < 0 ) // Not mutable?
|
||||||
|
// Not mutable: finish copy of word, and retry on copied word
|
||||||
|
return help_copy_impl(i).help_copy().add(i);
|
||||||
|
if( (old & mask) != 0 ) return false; // Bit is already set?
|
||||||
|
} while( !nbsi.CAS( j>>6, old, old | mask ) );
|
||||||
|
_size.add(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove( final int i ) {
|
||||||
|
if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array!
|
||||||
|
return _new==null ? false : help_copy().remove(i);
|
||||||
|
|
||||||
|
// Handle every 64th bit via using a nested array
|
||||||
|
NBSI nbsi = this; // The bit array being added into
|
||||||
|
int j = i; // The bit index being added
|
||||||
|
while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set)
|
||||||
|
nbsi = nbsi._nbsi64; // Recurse
|
||||||
|
j = j>>6; // Strip off low 6 bits (all set)
|
||||||
|
}
|
||||||
|
|
||||||
|
final long mask = mask(j);
|
||||||
|
long old;
|
||||||
|
do {
|
||||||
|
old = nbsi._bits[j>>6]; // Read old bits
|
||||||
|
if( old < 0 ) // Not mutable?
|
||||||
|
// Not mutable: finish copy of word, and retry on copied word
|
||||||
|
return help_copy_impl(i).help_copy().remove(i);
|
||||||
|
if( (old & mask) == 0 ) return false; // Bit is already clear?
|
||||||
|
} while( !nbsi.CAS( j>>6, old, old & ~mask ) );
|
||||||
|
_size.add(-1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains( final int i ) {
|
||||||
|
if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array!
|
||||||
|
return _new==null ? false : help_copy().contains(i);
|
||||||
|
|
||||||
|
// Handle every 64th bit via using a nested array
|
||||||
|
NBSI nbsi = this; // The bit array being added into
|
||||||
|
int j = i; // The bit index being added
|
||||||
|
while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set)
|
||||||
|
nbsi = nbsi._nbsi64; // Recurse
|
||||||
|
j = j>>6; // Strip off low 6 bits (all set)
|
||||||
|
}
|
||||||
|
|
||||||
|
final long mask = mask(j);
|
||||||
|
long old = nbsi._bits[j>>6]; // Read old bits
|
||||||
|
if( old < 0 ) // Not mutable?
|
||||||
|
// Not mutable: finish copy of word, and retry on copied word
|
||||||
|
return help_copy_impl(i).help_copy().contains(i);
|
||||||
|
// Yes mutable: test & return bit
|
||||||
|
return (old & mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise operations which store the result in this instance.
|
||||||
|
* Assumes that this instance contains ample buffer space to store the largest
|
||||||
|
* buffer from each NBSI in the recursive bitmap.
|
||||||
|
*
|
||||||
|
* Also assumes that this method is called during the construction process of
|
||||||
|
* the bitset before the instance could be leaked to multiple threads.
|
||||||
|
***/
|
||||||
|
public boolean intersect(NBSI dest, NBSI a, NBSI b) {
|
||||||
|
// terminate recursion if one bitset is missing data
|
||||||
|
// since that word should be left as 0L anyway
|
||||||
|
if(!has_bits(a) || !has_bits(b))
|
||||||
|
return true;
|
||||||
|
for(int i = 0; i < dest._bits.length; i++) {
|
||||||
|
long left = a.safe_read_word(i,0L);
|
||||||
|
long right = b.safe_read_word(i,0L);
|
||||||
|
dest._bits[i] = (left & right) & Long.MAX_VALUE; // mask sign bit
|
||||||
|
}
|
||||||
|
// todo - recompute size
|
||||||
|
return intersect(dest._nbsi64, a._nbsi64, b._nbsi64);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean union(NBSI dest, NBSI a, NBSI b) {
|
||||||
|
// terminate recursion if neiter bitset has data
|
||||||
|
if(!has_bits(a) && !has_bits(b))
|
||||||
|
return true;
|
||||||
|
if(has_bits(a) || has_bits(b)) {
|
||||||
|
for(int i = 0; i < dest._bits.length; i++) {
|
||||||
|
long left = a == null ? 0L : a.safe_read_word(i,0);
|
||||||
|
long right = b == null ? 0L : b.safe_read_word(i,0);
|
||||||
|
dest._bits[i] = (left | right) & Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return union(dest._nbsi64, a == null ? null : a._nbsi64, b == null ? null : b._nbsi64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
private long safe_read_word(int i, long default_word) {
|
||||||
|
if(i >= _bits.length) {
|
||||||
|
// allow reading past the end of the buffer filling in a default word
|
||||||
|
return default_word;
|
||||||
|
}
|
||||||
|
long word = _bits[i];
|
||||||
|
if(word < 0) {
|
||||||
|
NBSI nb = help_copy_impl(i);
|
||||||
|
if(nb._non_blocking_set_int == null) {
|
||||||
|
return default_word;
|
||||||
|
}
|
||||||
|
word = nb.help_copy()._bits[i];
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sizeInBytes() { return (int)_bits.length; }
|
||||||
|
|
||||||
|
public int size() { return (int)_size.get(); }
|
||||||
|
|
||||||
|
// Must grow the current array to hold an element of size i
|
||||||
|
private NBSI install_larger_new_bits( final int i ) {
|
||||||
|
if( _new == null ) {
|
||||||
|
// Grow by powers of 2, to avoid minor grow-by-1's.
|
||||||
|
// Note: must grow by exact powers-of-2 or the by-64-bit trick doesn't work right
|
||||||
|
int sz = (_bits.length<<6)<<1;
|
||||||
|
// CAS to install a new larger size. Did it work? Did it fail? We
|
||||||
|
// don't know and don't care. Only One can be installed, so if
|
||||||
|
// another thread installed a too-small size, we can't help it - we
|
||||||
|
// must simply install our new larger size as a nested-resize table.
|
||||||
|
CAS_new(new NBSI(sz, _size, _non_blocking_set_int));
|
||||||
|
}
|
||||||
|
// Return self for 'fluid' programming style
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help any top-level NBSI to copy until completed.
|
||||||
|
// Always return the _new version of *this* NBSI, in case we're nested.
|
||||||
|
private NBSI help_copy() {
|
||||||
|
// Pick some words to help with - but only help copy the top-level NBSI.
|
||||||
|
// Nested NBSI waits until the top is done before we start helping.
|
||||||
|
NBSI top_nbsi = _non_blocking_set_int._nbsi;
|
||||||
|
final int HELP = 8; // Tuning number: how much copy pain are we willing to inflict?
|
||||||
|
// We "help" by forcing individual bit indices to copy. However, bits
|
||||||
|
// come in lumps of 64 per word, so we just advance the bit counter by 64's.
|
||||||
|
int idx = top_nbsi._copyIdx.getAndAdd(64*HELP);
|
||||||
|
for( int i=0; i<HELP; i++ ) {
|
||||||
|
int j = idx+i*64;
|
||||||
|
j %= (top_nbsi._bits.length<<6); // Limit, wrap to array size; means we retry indices
|
||||||
|
top_nbsi.help_copy_impl(j );
|
||||||
|
top_nbsi.help_copy_impl(j+63); // Also force the nested-by-64 bit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top level guy ready to promote?
|
||||||
|
// Note: WE may not be the top-level guy!
|
||||||
|
if( top_nbsi._copyDone.get() == top_nbsi._sum_bits_length )
|
||||||
|
// One shot CAS to promote - it may fail since we are racing; others
|
||||||
|
// may promote as well
|
||||||
|
if( _non_blocking_set_int.CAS_nbsi( top_nbsi, top_nbsi._new ) ) {
|
||||||
|
//System.out.println("Promote at top level to size "+(_non_blocking_set_int._nbsi._bits.length<<6));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new bitvector for 'fluid' programming style
|
||||||
|
return _new;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help copy this one word. State Machine.
|
||||||
|
// (1) If not "made immutable" in the old array, set the sign bit to make
|
||||||
|
// it immutable.
|
||||||
|
// (2) If non-zero in old array & zero in new, CAS new from 0 to copy-of-old
|
||||||
|
// (3) If non-zero in old array & non-zero in new, CAS old to zero
|
||||||
|
// (4) Zero in old, new is valid
|
||||||
|
// At this point, old should be immutable-zero & new has a copy of bits
|
||||||
|
private NBSI help_copy_impl( int i ) {
|
||||||
|
// Handle every 64th bit via using a nested array
|
||||||
|
NBSI old = this; // The bit array being copied from
|
||||||
|
NBSI nnn = _new; // The bit array being copied to
|
||||||
|
if( nnn == null ) return this; // Promoted already
|
||||||
|
int j = i; // The bit index being added
|
||||||
|
while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set)
|
||||||
|
old = old._nbsi64; // Recurse
|
||||||
|
nnn = nnn._nbsi64; // Recurse
|
||||||
|
j = j>>6; // Strip off low 6 bits (all set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transit from state 1: word is not immutable yet
|
||||||
|
// Immutable is in bit 63, the sign bit.
|
||||||
|
long bits = old._bits[j>>6];
|
||||||
|
while( bits >= 0 ) { // Still in state (1)?
|
||||||
|
long oldbits = bits;
|
||||||
|
bits |= mask(63); // Target state of bits: sign-bit means immutable
|
||||||
|
if( old.CAS( j>>6, oldbits, bits ) ) {
|
||||||
|
if( oldbits == 0 ) _copyDone.addAndGet(1);
|
||||||
|
break; // Success - old array word is now immutable
|
||||||
|
}
|
||||||
|
bits = old._bits[j>>6]; // Retry if CAS failed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transit from state 2: non-zero in old and zero in new
|
||||||
|
if( bits != mask(63) ) { // Non-zero in old?
|
||||||
|
long new_bits = nnn._bits[j>>6];
|
||||||
|
if( new_bits == 0 ) { // New array is still zero
|
||||||
|
new_bits = bits & ~mask(63); // Desired new value: a mutable copy of bits
|
||||||
|
// One-shot CAS attempt, no loop, from 0 to non-zero.
|
||||||
|
// If it fails, somebody else did the copy for us
|
||||||
|
if( !nnn.CAS( j>>6, 0, new_bits ) )
|
||||||
|
new_bits = nnn._bits[j>>6]; // Since it failed, get the new value
|
||||||
|
assert new_bits != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transit from state 3: non-zero in old and non-zero in new
|
||||||
|
// One-shot CAS attempt, no loop, from non-zero to 0 (but immutable)
|
||||||
|
if( old.CAS( j>>6, bits, mask(63) ) )
|
||||||
|
_copyDone.addAndGet(1); // One more word finished copying
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now in state 4: zero (and immutable) in old
|
||||||
|
|
||||||
|
// Return the self bitvector for 'fluid' programming style
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void print( int d, String msg ) {
|
||||||
|
for( int i=0; i<d; i++ )
|
||||||
|
System.out.print(" ");
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void print(int d) {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("NBSI - _bits.len=");
|
||||||
|
NBSI x = this;
|
||||||
|
while( x != null ) {
|
||||||
|
buf.append(" "+x._bits.length);
|
||||||
|
x = x._nbsi64;
|
||||||
|
}
|
||||||
|
print(d,buf.toString());
|
||||||
|
|
||||||
|
x = this;
|
||||||
|
while( x != null ) {
|
||||||
|
for( int i=0; i<x._bits.length; i++ )
|
||||||
|
System.out.print(Long.toHexString(x._bits[i])+" ");
|
||||||
|
x = x._nbsi64;
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _copyIdx.get() != 0 || _copyDone.get() != 0 )
|
||||||
|
print(d,"_copyIdx="+_copyIdx.get()+" _copyDone="+_copyDone.get()+" _words_to_cpy="+_sum_bits_length);
|
||||||
|
if( _new != null ) {
|
||||||
|
print(d,"__has_new - ");
|
||||||
|
_new.print(d+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
module common.utils {
|
|
||||||
exports org.warp.commonutils.moshi;
|
|
||||||
exports org.warp.commonutils.type;
|
|
||||||
exports org.warp.commonutils.range;
|
|
||||||
exports org.warp.commonutils.error;
|
|
||||||
exports org.warp.commonutils.random;
|
|
||||||
exports org.warp.commonutils.functional;
|
|
||||||
exports edu.rice.cs.util;
|
|
||||||
exports org.warp.commonutils.locks;
|
|
||||||
requires org.jetbrains.annotations;
|
|
||||||
requires it.unimi.dsi.fastutil;
|
|
||||||
requires moshi;
|
|
||||||
requires moshi.records.reflect;
|
|
||||||
requires org.apache.commons.lang3;
|
|
||||||
requires okio;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package org.warp.commonutils.error;
|
|
||||||
|
|
||||||
public class IndexOutOfBoundsException extends RuntimeException {
|
|
||||||
|
|
||||||
public IndexOutOfBoundsException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexOutOfBoundsException(String s) {
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexOutOfBoundsException(long index) {
|
|
||||||
super("Index out of range: " + index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexOutOfBoundsException(long index, long min, long max) {
|
|
||||||
super("Index " + index + " out of range (from " + min + " to " + max + ")");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOBiConsumer<T, U> {
|
|
||||||
|
|
||||||
void consume(T t, U u) throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOBooleanSupplier {
|
|
||||||
|
|
||||||
boolean get() throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public interface IOCompletableFunction<T, U> {
|
|
||||||
CompletableFuture<U> apply(T value) throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOConsumer<T> {
|
|
||||||
|
|
||||||
void consume(T value) throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOFunction<T, U> {
|
|
||||||
|
|
||||||
U apply(T data) throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOIntegerSupplier {
|
|
||||||
|
|
||||||
int get() throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOLongSupplier {
|
|
||||||
|
|
||||||
long get() throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IORunnable {
|
|
||||||
|
|
||||||
void run() throws IOException;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public interface IOSupplier<T> {
|
|
||||||
|
|
||||||
T get() throws IOException;
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an operation that accepts three input arguments and returns no
|
|
||||||
* result. This is the three-arity specialization of {@link Consumer}.
|
|
||||||
* Unlike most other functional interfaces, {@code TriConsumer} is expected
|
|
||||||
* to operate via side-effects.
|
|
||||||
*
|
|
||||||
* <p>This is a <a href="package-summary.html">functional interface</a>
|
|
||||||
* whose functional method is {@link #accept(Object, Object, Object)}.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the first argument to the operation
|
|
||||||
* @param <U> the type of the second argument to the operation
|
|
||||||
* @param <U> the type of the thord argument to the operation
|
|
||||||
*
|
|
||||||
* @see Consumer
|
|
||||||
* @since 1.8
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface IOTriConsumer<T, U, V> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs this operation on the given arguments.
|
|
||||||
*
|
|
||||||
* @param t the first input argument
|
|
||||||
* @param u the second input argument
|
|
||||||
* @param v the third input argument
|
|
||||||
*/
|
|
||||||
void accept(T t, U u, V v) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed {@code TriConsumer} that performs, in sequence, this
|
|
||||||
* operation followed by the {@code after} operation. If performing either
|
|
||||||
* operation throws an exception, it is relayed to the caller of the
|
|
||||||
* composed operation. If performing this operation throws an exception,
|
|
||||||
* the {@code after} operation will not be performed.
|
|
||||||
*
|
|
||||||
* @param after the operation to perform after this operation
|
|
||||||
* @return a composed {@code TriConsumer} that performs in sequence this
|
|
||||||
* operation followed by the {@code after} operation
|
|
||||||
* @throws NullPointerException if {@code after} is null
|
|
||||||
*/
|
|
||||||
default IOTriConsumer<T, U, V> andThen(IOTriConsumer<? super T, ? super U, ? super V> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
|
|
||||||
return (l, r, u) -> {
|
|
||||||
accept(l, r, u);
|
|
||||||
after.accept(l, r, u);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an operation that accepts three input arguments and returns no
|
|
||||||
* result. This is the three-arity specialization of {@link Consumer}.
|
|
||||||
* Unlike most other functional interfaces, {@code TriConsumer} is expected
|
|
||||||
* to operate via side-effects.
|
|
||||||
*
|
|
||||||
* <p>This is a <a href="package-summary.html">functional interface</a>
|
|
||||||
* whose functional method is {@link #accept(Object, Object, Object)}.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the first argument to the operation
|
|
||||||
* @param <U> the type of the second argument to the operation
|
|
||||||
* @param <U> the type of the thord argument to the operation
|
|
||||||
*
|
|
||||||
* @see Consumer
|
|
||||||
* @since 1.8
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface TriConsumer<T, U, V> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs this operation on the given arguments.
|
|
||||||
*
|
|
||||||
* @param t the first input argument
|
|
||||||
* @param u the second input argument
|
|
||||||
* @param v the third input argument
|
|
||||||
*/
|
|
||||||
void accept(T t, U u, V v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed {@code TriConsumer} that performs, in sequence, this
|
|
||||||
* operation followed by the {@code after} operation. If performing either
|
|
||||||
* operation throws an exception, it is relayed to the caller of the
|
|
||||||
* composed operation. If performing this operation throws an exception,
|
|
||||||
* the {@code after} operation will not be performed.
|
|
||||||
*
|
|
||||||
* @param after the operation to perform after this operation
|
|
||||||
* @return a composed {@code TriConsumer} that performs in sequence this
|
|
||||||
* operation followed by the {@code after} operation
|
|
||||||
* @throws NullPointerException if {@code after} is null
|
|
||||||
*/
|
|
||||||
default org.warp.commonutils.functional.TriConsumer<T, U, V> andThen(org.warp.commonutils.functional.TriConsumer<? super T, ? super U, ? super V> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
|
|
||||||
return (l, r, u) -> {
|
|
||||||
accept(l, r, u);
|
|
||||||
after.accept(l, r, u);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package org.warp.commonutils.functional;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a function that accepts three arguments and produces a result.
|
|
||||||
* This is the three-arity specialization of {@link Function}.
|
|
||||||
*
|
|
||||||
* <p>This is a <a href="package-summary.html">functional interface</a>
|
|
||||||
* whose functional method is {@link #apply(Object, Object, Object)}.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the first argument to the function
|
|
||||||
* @param <U> the type of the second argument to the function
|
|
||||||
* @param <X> the type of the third argument to the function
|
|
||||||
* @param <R> the type of the result of the function
|
|
||||||
*
|
|
||||||
* @see Function
|
|
||||||
* @since 1.8
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface TriFunction<T, U, X, R> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies this function to the given arguments.
|
|
||||||
*
|
|
||||||
* @param t the first function argument
|
|
||||||
* @param u the second function argument
|
|
||||||
* @param x the third function argument
|
|
||||||
* @return the function result
|
|
||||||
*/
|
|
||||||
R apply(T t, U u, X x);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a composed function that first applies this function to
|
|
||||||
* its input, and then applies the {@code after} function to the result.
|
|
||||||
* If evaluation of either function throws an exception, it is relayed to
|
|
||||||
* the caller of the composed function.
|
|
||||||
*
|
|
||||||
* @param <V> the type of output of the {@code after} function, and of the
|
|
||||||
* composed function
|
|
||||||
* @param after the function to apply after this function is applied
|
|
||||||
* @return a composed function that first applies this function and then
|
|
||||||
* applies the {@code after} function
|
|
||||||
* @throws NullPointerException if after is null
|
|
||||||
*/
|
|
||||||
default <V> org.warp.commonutils.functional.TriFunction<T, U, X, V> andThen(Function<? super R, ? extends V> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
return (T t, U u, X x) -> after.apply(apply(t, u, x));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
package org.warp.commonutils.locks;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.warp.commonutils.functional.IORunnable;
|
|
||||||
import org.warp.commonutils.functional.IOSupplier;
|
|
||||||
|
|
||||||
public class LockUtils {
|
|
||||||
|
|
||||||
public static void lock(@Nullable Lock lock, @NotNull Runnable r) {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void readLock(@Nullable StampedLock lock, @NotNull Runnable r) {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.readLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockRead(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeLock(@Nullable StampedLock lock, @NotNull Runnable r) {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.writeLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockWrite(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void lockIO(@Nullable Lock lock, @NotNull IORunnable r) throws IOException {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void readLockIO(@Nullable StampedLock lock, @NotNull IORunnable r) throws IOException {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.readLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockRead(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeLockIO(@Nullable StampedLock lock, @NotNull IORunnable r) throws IOException {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.writeLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockWrite(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T lock(@Nullable Lock lock, @NotNull Supplier<T> r) {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T readLock(@Nullable StampedLock lock, @NotNull Supplier<T> r) {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.readLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockRead(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T writeLock(@Nullable StampedLock lock, @NotNull Supplier<T> r) {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.writeLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockWrite(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T lockIO(@Nullable Lock lock, @NotNull IOSupplier<T> r) throws IOException {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T readLockIO(@Nullable StampedLock lock, @NotNull IOSupplier<T> r) throws IOException {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.readLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockRead(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T writeLockIO(@Nullable StampedLock lock, @NotNull IOSupplier<T> r) throws IOException {
|
|
||||||
long lockValue;
|
|
||||||
if (lock != null) {
|
|
||||||
lockValue = lock.writeLock();
|
|
||||||
} else {
|
|
||||||
lockValue = 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return r.get();
|
|
||||||
} finally {
|
|
||||||
if (lock != null) {
|
|
||||||
lock.unlockWrite(lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.booleans.BooleanList;
|
|
||||||
import it.unimi.dsi.fastutil.booleans.BooleanLists;
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class BooleanListJsonAdapter extends JsonAdapter<BooleanList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull BooleanList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
BooleanArrayList modifiableOutput = new BooleanArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add(reader.nextBoolean());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return BooleanLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable BooleanList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value(value.getBoolean(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteLists;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class ByteListJsonAdapter extends JsonAdapter<ByteList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ByteList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
ByteArrayList modifiableOutput = new ByteArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add((byte) reader.nextInt());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return ByteLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable ByteList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value((long) value.getByte(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.chars.CharList;
|
|
||||||
import it.unimi.dsi.fastutil.chars.CharLists;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class CharListJsonAdapter extends JsonAdapter<CharList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull CharList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
CharArrayList modifiableOutput = new CharArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add((char) reader.nextInt());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return CharLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable CharList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value((long) value.getChar(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntLists;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class IntListJsonAdapter extends JsonAdapter<IntList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull IntList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
IntArrayList modifiableOutput = new IntArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add(reader.nextInt());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return IntLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable IntList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value((long) value.getInt(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongLists;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class LongListJsonAdapter extends JsonAdapter<LongList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull LongList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
LongArrayList modifiableOutput = new LongArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add(reader.nextLong());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return LongLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable LongList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value(value.getLong(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,373 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonDataException;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonReader.Options;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import com.squareup.moshi.Moshi;
|
|
||||||
import com.squareup.moshi.Types;
|
|
||||||
import dev.zacsweers.moshix.records.RecordsJsonAdapterFactory;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.warp.commonutils.serialization.UTFUtils;
|
|
||||||
|
|
||||||
public abstract class MoshiPolymorphic<OBJ> {
|
|
||||||
|
|
||||||
public enum GetterStyle {
|
|
||||||
FIELDS,
|
|
||||||
RECORDS_GETTERS,
|
|
||||||
STANDARD_GETTERS
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean instantiateUsingStaticOf;
|
|
||||||
private final GetterStyle getterStyle;
|
|
||||||
private boolean initialized = false;
|
|
||||||
private Moshi abstractMoshi;
|
|
||||||
private final Map<Type, JsonAdapter<OBJ>> abstractClassesSerializers = new ConcurrentHashMap<>();
|
|
||||||
private final Map<Type, JsonAdapter<List<OBJ>>> abstractListClassesSerializers = new ConcurrentHashMap<>();
|
|
||||||
private final Map<Type, JsonAdapter<OBJ>> concreteClassesSerializers = new ConcurrentHashMap<>();
|
|
||||||
private final Map<Type, JsonAdapter<List<OBJ>>> concreteListClassesSerializers = new ConcurrentHashMap<>();
|
|
||||||
private final Map<Type, JsonAdapter<?>> extraClassesSerializers = new ConcurrentHashMap<>();
|
|
||||||
private final Map<String, JsonAdapter<OBJ>> customAdapters = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public MoshiPolymorphic() {
|
|
||||||
this(false, GetterStyle.FIELDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MoshiPolymorphic(boolean instantiateUsingStaticOf, GetterStyle getterStyle) {
|
|
||||||
this.instantiateUsingStaticOf = instantiateUsingStaticOf;
|
|
||||||
this.getterStyle = getterStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void initialize() {
|
|
||||||
if (!this.initialized) {
|
|
||||||
this.initialized = true;
|
|
||||||
var abstractMoshiBuilder = new Moshi.Builder();
|
|
||||||
var abstractClasses = getAbstractClasses();
|
|
||||||
var concreteClasses = getConcreteClasses();
|
|
||||||
var extraAdapters = getExtraAdapters();
|
|
||||||
|
|
||||||
extraAdapters.forEach((extraClass, jsonAdapter) -> {
|
|
||||||
extraClassesSerializers.put(extraClass, jsonAdapter);
|
|
||||||
abstractMoshiBuilder.add(extraClass, jsonAdapter);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (Class<?> declaredClass : abstractClasses) {
|
|
||||||
var name = fixType(declaredClass.getSimpleName());
|
|
||||||
JsonAdapter<OBJ> adapter = new PolymorphicAdapter<>(name);
|
|
||||||
if (!extraClassesSerializers.containsKey(declaredClass)) {
|
|
||||||
abstractMoshiBuilder.add(declaredClass, adapter);
|
|
||||||
abstractClassesSerializers.put(declaredClass, adapter);
|
|
||||||
abstractListClassesSerializers.put(Types.newParameterizedType(List.class, declaredClass),
|
|
||||||
new ListValueAdapter<>(adapter)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
customAdapters.put(name, adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Class<?> declaredClass : concreteClasses) {
|
|
||||||
var name = fixType(declaredClass.getSimpleName());
|
|
||||||
JsonAdapter<OBJ> adapter = new NormalValueAdapter<>(name, declaredClass);
|
|
||||||
if (!extraClassesSerializers.containsKey(declaredClass)
|
|
||||||
&& !abstractClassesSerializers.containsKey(declaredClass)) {
|
|
||||||
concreteClassesSerializers.put(declaredClass, adapter);
|
|
||||||
concreteListClassesSerializers.put(Types.newParameterizedType(List.class, declaredClass),
|
|
||||||
new ListValueAdapter<>(adapter)
|
|
||||||
);
|
|
||||||
abstractMoshiBuilder.add(declaredClass, adapter);
|
|
||||||
}
|
|
||||||
customAdapters.put(name, adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstractMoshiBuilder.addLast(new RecordsJsonAdapterFactory());
|
|
||||||
|
|
||||||
abstractMoshi = abstractMoshiBuilder.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Set<Class<OBJ>> getAbstractClasses();
|
|
||||||
|
|
||||||
protected abstract Set<Class<OBJ>> getConcreteClasses();
|
|
||||||
|
|
||||||
protected Map<Class<?>, JsonAdapter<?>> getExtraAdapters() {
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract boolean shouldIgnoreField(String fieldName);
|
|
||||||
|
|
||||||
public Moshi.Builder registerAdapters(Moshi.Builder moshiBuilder) {
|
|
||||||
initialize();
|
|
||||||
extraClassesSerializers.forEach(moshiBuilder::add);
|
|
||||||
abstractClassesSerializers.forEach(moshiBuilder::add);
|
|
||||||
abstractListClassesSerializers.forEach(moshiBuilder::add);
|
|
||||||
concreteClassesSerializers.forEach(moshiBuilder::add);
|
|
||||||
concreteListClassesSerializers.forEach(moshiBuilder::add);
|
|
||||||
return moshiBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PolymorphicAdapter<T> extends JsonAdapter<T> {
|
|
||||||
|
|
||||||
private final String adapterName;
|
|
||||||
|
|
||||||
private PolymorphicAdapter(String adapterName) {
|
|
||||||
this.adapterName = adapterName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Options NAMES = Options.of("type", "properties");
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public T fromJson(@NotNull JsonReader jsonReader) throws IOException {
|
|
||||||
String type = null;
|
|
||||||
|
|
||||||
jsonReader.beginObject();
|
|
||||||
iterate: while (jsonReader.hasNext()) {
|
|
||||||
switch (jsonReader.selectName(NAMES)) {
|
|
||||||
case 0:
|
|
||||||
type = fixType(jsonReader.nextString());
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (type == null) {
|
|
||||||
throw new JsonDataException("Type must be defined before properties");
|
|
||||||
}
|
|
||||||
break iterate;
|
|
||||||
default:
|
|
||||||
String name = jsonReader.nextName();
|
|
||||||
throw new JsonDataException("Key \"" + name + "\" is invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonAdapter<? extends OBJ> propertiesAdapter = customAdapters.get(type);
|
|
||||||
if (propertiesAdapter == null) {
|
|
||||||
throw new JsonDataException("Type \"" + type + "\" is unknown");
|
|
||||||
}
|
|
||||||
//noinspection unchecked
|
|
||||||
var result = (T) propertiesAdapter.fromJson(jsonReader);
|
|
||||||
|
|
||||||
jsonReader.endObject();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter jsonWriter, @Nullable T t) throws IOException {
|
|
||||||
if (t == null) {
|
|
||||||
jsonWriter.nullValue();
|
|
||||||
} else {
|
|
||||||
String type = fixType(t.getClass().getSimpleName());
|
|
||||||
|
|
||||||
JsonAdapter<OBJ> propertiesAdapter = customAdapters.get(type);
|
|
||||||
if (propertiesAdapter == null) {
|
|
||||||
abstractMoshi.adapter(java.lang.Object.class).toJson(jsonWriter, t);
|
|
||||||
} else {
|
|
||||||
jsonWriter.beginObject();
|
|
||||||
jsonWriter.name("type").value(type);
|
|
||||||
jsonWriter.name("properties");
|
|
||||||
//noinspection unchecked
|
|
||||||
propertiesAdapter.toJson(jsonWriter, (OBJ) t);
|
|
||||||
jsonWriter.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NormalValueAdapter<T> extends JsonAdapter<T> {
|
|
||||||
|
|
||||||
private final String adapterName;
|
|
||||||
private final Options names;
|
|
||||||
private final Class<?> declaredClass;
|
|
||||||
private final List<Field> declaredFields;
|
|
||||||
private final Function<T, Object>[] fieldGetters;
|
|
||||||
|
|
||||||
private NormalValueAdapter(String adapterName, Class<?> declaredClass) {
|
|
||||||
try {
|
|
||||||
this.adapterName = adapterName;
|
|
||||||
this.declaredClass = declaredClass;
|
|
||||||
this.declaredFields = Arrays
|
|
||||||
.stream(declaredClass.getDeclaredFields())
|
|
||||||
.filter(field -> {
|
|
||||||
var modifiers = field.getModifiers();
|
|
||||||
return !Modifier.isStatic(modifiers)
|
|
||||||
&& !Modifier.isTransient(modifiers)
|
|
||||||
&& !shouldIgnoreField(field.getName());
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
String[] fieldNames = new String[this.declaredFields.size()];
|
|
||||||
//noinspection unchecked
|
|
||||||
this.fieldGetters = new Function[this.declaredFields.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (Field declaredField : this.declaredFields) {
|
|
||||||
fieldNames[i] = declaredField.getName();
|
|
||||||
|
|
||||||
switch (getterStyle) {
|
|
||||||
case STANDARD_GETTERS:
|
|
||||||
var getterMethod = declaredField
|
|
||||||
.getDeclaringClass()
|
|
||||||
.getMethod("get" + StringUtils.capitalize(declaredField.getName()));
|
|
||||||
fieldGetters[i] = obj -> {
|
|
||||||
try {
|
|
||||||
return getterMethod.invoke(obj);
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case RECORDS_GETTERS:
|
|
||||||
var getterMethod2 = declaredField
|
|
||||||
.getDeclaringClass()
|
|
||||||
.getMethod(declaredField.getName());
|
|
||||||
fieldGetters[i] = obj -> {
|
|
||||||
try {
|
|
||||||
return getterMethod2.invoke(obj);
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case FIELDS:
|
|
||||||
fieldGetters[i] = t -> {
|
|
||||||
try {
|
|
||||||
return declaredField.get(t);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
} this.names = Options.of(fieldNames);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public T fromJson(@NotNull JsonReader jsonReader) throws IOException {
|
|
||||||
try {
|
|
||||||
Object instance;
|
|
||||||
Object[] fields;
|
|
||||||
if (instantiateUsingStaticOf) {
|
|
||||||
fields = new Object[declaredFields.size()];
|
|
||||||
instance = null;
|
|
||||||
} else {
|
|
||||||
fields = null;
|
|
||||||
instance = declaredClass.getConstructor().newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonReader.beginObject();
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
var nameId = jsonReader.selectName(names);
|
|
||||||
if (nameId >= 0 && nameId < this.declaredFields.size()) {
|
|
||||||
var fieldValue = abstractMoshi.adapter(declaredFields.get(nameId).getGenericType()).fromJson(jsonReader);
|
|
||||||
if (instantiateUsingStaticOf) {
|
|
||||||
fields[nameId] = fieldValue;
|
|
||||||
} else {
|
|
||||||
declaredFields.get(nameId).set(instance, fieldValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String keyName = jsonReader.nextName();
|
|
||||||
throw new JsonDataException("Key \"" + keyName + "\" is invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsonReader.endObject();
|
|
||||||
|
|
||||||
if (instantiateUsingStaticOf) {
|
|
||||||
Class[] params = new Class[declaredFields.size()];
|
|
||||||
for (int i = 0; i < declaredFields.size(); i++) {
|
|
||||||
params[i] = declaredFields.get(i).getType();
|
|
||||||
}
|
|
||||||
instance = declaredClass.getMethod("of", params).invoke(null, fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
return (T) instance;
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
|
||||||
throw new JsonDataException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter jsonWriter, @Nullable T t) throws IOException {
|
|
||||||
if (t == null) {
|
|
||||||
jsonWriter.nullValue();
|
|
||||||
} else {
|
|
||||||
jsonWriter.beginObject();
|
|
||||||
int i = 0;
|
|
||||||
for (Field declaredField : declaredFields) {
|
|
||||||
jsonWriter.name(declaredField.getName());
|
|
||||||
Class<?> fieldType = declaredField.getType();
|
|
||||||
if (abstractClassesSerializers.containsKey(fieldType)) {
|
|
||||||
//noinspection unchecked
|
|
||||||
abstractClassesSerializers.<OBJ>get(fieldType).toJson(jsonWriter, (OBJ) fieldGetters[i].apply(t));
|
|
||||||
} else if (concreteClassesSerializers.containsKey(fieldType)) {
|
|
||||||
//noinspection unchecked
|
|
||||||
concreteClassesSerializers.<OBJ>get(fieldType).toJson(jsonWriter, (OBJ) fieldGetters[i].apply(t));
|
|
||||||
} else {
|
|
||||||
abstractMoshi.<Object>adapter(fieldType).toJson(jsonWriter, fieldGetters[i].apply(t));
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
jsonWriter.endObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListValueAdapter<T> extends JsonAdapter<List<T>> {
|
|
||||||
|
|
||||||
private final JsonAdapter<T> valueAdapter;
|
|
||||||
|
|
||||||
public ListValueAdapter(JsonAdapter<T> valueAdapter) {
|
|
||||||
this.valueAdapter = valueAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<T> fromJson(@NotNull JsonReader jsonReader) throws IOException {
|
|
||||||
jsonReader.beginArray();
|
|
||||||
var result = new ArrayList<T>();
|
|
||||||
while (jsonReader.hasNext()) {
|
|
||||||
result.add(valueAdapter.fromJson(jsonReader));
|
|
||||||
}
|
|
||||||
jsonReader.endArray();
|
|
||||||
return Collections.unmodifiableList(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter jsonWriter, @Nullable List<T> ts) throws IOException {
|
|
||||||
if (ts == null) {
|
|
||||||
jsonWriter.nullValue();
|
|
||||||
} else {
|
|
||||||
jsonWriter.beginArray();
|
|
||||||
for (T value : ts) {
|
|
||||||
valueAdapter.toJson(jsonWriter.valueSink(), value);
|
|
||||||
}
|
|
||||||
jsonWriter.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fixType(String nextString) {
|
|
||||||
if (nextString.length() > 512) {
|
|
||||||
throw new IllegalArgumentException("Input too long: " + nextString.length());
|
|
||||||
}
|
|
||||||
return UTFUtils.keepOnlyASCII(nextString);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.warp.commonutils.moshi;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.JsonReader;
|
|
||||||
import com.squareup.moshi.JsonWriter;
|
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortLists;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class ShortListJsonAdapter extends JsonAdapter<ShortList> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ShortList fromJson(@NotNull JsonReader reader) throws IOException {
|
|
||||||
reader.beginArray();
|
|
||||||
ShortArrayList modifiableOutput = new ShortArrayList();
|
|
||||||
while (reader.hasNext()) {
|
|
||||||
modifiableOutput.add((short) reader.nextInt());
|
|
||||||
}
|
|
||||||
reader.endArray();
|
|
||||||
return ShortLists.unmodifiable(modifiableOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toJson(@NotNull JsonWriter writer, @Nullable ShortList value) throws IOException {
|
|
||||||
if (value == null) {
|
|
||||||
writer.nullValue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.beginArray();
|
|
||||||
for (int i = 0; i < value.size(); i++) {
|
|
||||||
writer.value((long) value.getShort(i));
|
|
||||||
}
|
|
||||||
writer.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
package org.warp.commonutils.random;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Random;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Linear feedback shift register
|
|
||||||
* <p>
|
|
||||||
* Taps can be found at: See http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf See
|
|
||||||
* http://mathoverflow.net/questions/46961/how-are-taps-proven-to-work-for-lfsrs/46983#46983 See
|
|
||||||
* http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm See
|
|
||||||
* http://www.yikes.com/~ptolemy/lfsr_web/index.htm See http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html
|
|
||||||
*
|
|
||||||
* @author OldCurmudgeon
|
|
||||||
*/
|
|
||||||
public class LFSR implements Iterable<BigInteger> {
|
|
||||||
|
|
||||||
private static final Random random = new Random();
|
|
||||||
|
|
||||||
// Bit pattern for taps.
|
|
||||||
private final BigInteger taps;
|
|
||||||
// Where to start (and end).
|
|
||||||
private final BigInteger start;
|
|
||||||
|
|
||||||
public static LFSR randomInt() {
|
|
||||||
return random(32, random.nextInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LFSR randomLong() {
|
|
||||||
return random(64, random.nextLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LFSR randomPositiveLong() {
|
|
||||||
return random(50, Math.abs(random.nextInt()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BigInteger randomPrimitive(int bitsSize) {
|
|
||||||
// Build the BigInteger.
|
|
||||||
BigInteger primitive = BigInteger.ZERO;
|
|
||||||
for (int bitNumber = 0; bitNumber <= bitsSize; bitNumber++) {
|
|
||||||
if (random.nextBoolean() || bitNumber == 0 || bitNumber == bitsSize) {
|
|
||||||
primitive = primitive.or(BigInteger.ONE.shiftLeft(bitNumber));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return primitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LFSR random(int bitsSize, long startNumber) {
|
|
||||||
return new LFSR(randomPrimitive(bitsSize), BigInteger.valueOf(startNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The poly must be primitive to span the full sequence.
|
|
||||||
public LFSR(BigInteger primitivePoly, BigInteger start) {
|
|
||||||
// Where to start from (and stop).
|
|
||||||
this.start = start.equals(BigInteger.ZERO) ? BigInteger.ONE : start;
|
|
||||||
// Knock off the 2^0 coefficient of the polynomial for the TAP.
|
|
||||||
this.taps = primitivePoly.shiftRight(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public LFSRIterator iterator() {
|
|
||||||
return new LFSRIterator(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LFSRIterator implements Iterator<BigInteger> {
|
|
||||||
// The last one we returned.
|
|
||||||
|
|
||||||
private BigInteger last = null;
|
|
||||||
// The next one to return.
|
|
||||||
private BigInteger next = null;
|
|
||||||
|
|
||||||
public LFSRIterator(BigInteger start) {
|
|
||||||
// Do not return the seed.
|
|
||||||
last = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (next == null) {
|
|
||||||
/*
|
|
||||||
* Uses the Galois form.
|
|
||||||
*
|
|
||||||
* Shift last right one.
|
|
||||||
*
|
|
||||||
* If the bit shifted out was a 1 - xor with the tap mask.
|
|
||||||
*/
|
|
||||||
boolean shiftedOutA1 = last.testBit(0);
|
|
||||||
// Shift right.
|
|
||||||
next = last.shiftRight(1);
|
|
||||||
if (shiftedOutA1) {
|
|
||||||
// Tap!
|
|
||||||
next = next.xor(taps);
|
|
||||||
}
|
|
||||||
// Never give them `start` again.
|
|
||||||
if (next.equals(start)) {
|
|
||||||
// Could set a finished flag here too.
|
|
||||||
next = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return next != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BigInteger next() {
|
|
||||||
// Remember this one.
|
|
||||||
last = hasNext() ? next : null;
|
|
||||||
// Don't deliver it again.
|
|
||||||
next = null;
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BigInteger next(BigInteger last) {
|
|
||||||
this.last = last;
|
|
||||||
next = null;
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException("Not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return LFSR.this.toString() + "[" + (last != null ? last.toString(16) : "") + "-" + (next != null ? next
|
|
||||||
.toString(16) : "") + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "(" + taps.toString(32) + ")-" + start.toString(32);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
package org.warp.commonutils.range;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class MappedRanges<T> {
|
|
||||||
|
|
||||||
private final Object2ObjectMap<Range, T> ranges;
|
|
||||||
|
|
||||||
public MappedRanges(int start, int end, T value) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
this.ranges = new Object2ObjectOpenHashMap<>();
|
|
||||||
ranges.put(new Range(start, end), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteRange(final int start, final int end, Function<T, T> replaceWhenSplitting, Function<T, T> cloneWhenSplitting) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
Object2ObjectOpenHashMap<Range, T> rangesToAdd = new Object2ObjectOpenHashMap<>();
|
|
||||||
ObjectOpenHashSet<Range> rangesToDelete = new ObjectOpenHashSet<>();
|
|
||||||
ranges.forEach((range, value) -> {
|
|
||||||
if (range.start <= end && range.end >= start) {
|
|
||||||
if (range.start >= start && range.end <= end) {
|
|
||||||
// delete the range
|
|
||||||
rangesToDelete.add(range);
|
|
||||||
} else if (range.start <= start && range.end >= end) {
|
|
||||||
// cut the hole
|
|
||||||
rangesToDelete.add(range);
|
|
||||||
rangesToAdd.put(new Range(range.start, start), value);
|
|
||||||
rangesToAdd.put(new Range(end, range.end), cloneWhenSplitting.apply(value));
|
|
||||||
} else if (range.start <= start && range.end <= end && range.end > start) {
|
|
||||||
// shrink the right border
|
|
||||||
rangesToDelete.add(range);
|
|
||||||
rangesToAdd.put(new Range(range.start, start), value);
|
|
||||||
} else if (range.start >= start && range.end >= end && range.start < end) {
|
|
||||||
// shrink the left border
|
|
||||||
rangesToDelete.add(range);
|
|
||||||
rangesToAdd.put(new Range(end, range.end), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (Range range : rangesToDelete) {
|
|
||||||
ranges.remove(range);
|
|
||||||
}
|
|
||||||
rangesToAdd.forEach((range, value) -> {
|
|
||||||
if (canAddRange(range)) {
|
|
||||||
ranges.put(range, replaceWhenSplitting.apply(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void transformRange(int start, int end, Function<T, T> replaceWhenOverlapping, Function<T, T> cloneWhenSplitting) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
Object2ObjectOpenHashMap<Range, T> rangesToTransform = new Object2ObjectOpenHashMap<>();
|
|
||||||
Object2ObjectOpenHashMap<Range, T> rangesToAdd = new Object2ObjectOpenHashMap<>();
|
|
||||||
ObjectOpenHashSet<Range> rangesToRemove = new ObjectOpenHashSet<>();
|
|
||||||
ranges.forEach((range, value) -> {
|
|
||||||
if (range.start <= end && range.end >= start) {
|
|
||||||
if (range.start >= start && range.end <= end) {
|
|
||||||
// transform the range
|
|
||||||
rangesToTransform.put(range, replaceWhenOverlapping.apply(value));
|
|
||||||
} else if (range.start <= start && range.end >= end) {
|
|
||||||
// transform the middle
|
|
||||||
rangesToRemove.add(range);
|
|
||||||
rangesToAdd.put(new Range(range.start, start), value);
|
|
||||||
rangesToTransform.put(new Range(start, end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
|
|
||||||
rangesToAdd.put(new Range(end, range.end), cloneWhenSplitting.apply(value));
|
|
||||||
} else if (range.start <= start && range.end <= end && range.end > start) {
|
|
||||||
// transform the right
|
|
||||||
rangesToRemove.add(range);
|
|
||||||
rangesToAdd.put(new Range(range.start, start), value);
|
|
||||||
rangesToTransform.put(new Range(start, range.end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
|
|
||||||
} else if (range.start >= start && range.end >= end && range.start < end) {
|
|
||||||
// transform the left
|
|
||||||
rangesToRemove.add(range);
|
|
||||||
rangesToTransform.put(new Range(range.start, end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
|
|
||||||
rangesToAdd.put(new Range(end, range.end), value);
|
|
||||||
} else {
|
|
||||||
// do not transform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rangesToRemove.forEach((range) -> {
|
|
||||||
ranges.remove(range);
|
|
||||||
});
|
|
||||||
rangesToAdd.forEach((range, value) -> {
|
|
||||||
if (canAddRange(range)) {
|
|
||||||
ranges.put(range, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
rangesToTransform.forEach((range, value) -> {
|
|
||||||
ranges.put(range, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canAddRange(UnmodifiableRange range) {
|
|
||||||
return range.getStart() != range.getEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canAddRange(Range range) {
|
|
||||||
return range.getStart() != range.getEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object2ObjectMap<UnmodifiableRange, T> getRanges() {
|
|
||||||
Object2ObjectSortedMap<UnmodifiableRange, T> a = new Object2ObjectRBTreeMap<>(Comparator.comparingLong(UnmodifiableRange::getStart));
|
|
||||||
ranges.forEach((range, value) -> a.put(range.unmodifiableClone(), value));
|
|
||||||
return Object2ObjectMaps.unmodifiable(a);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package org.warp.commonutils.range;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
|
||||||
|
|
||||||
public class Range {
|
|
||||||
|
|
||||||
public long start;
|
|
||||||
public long end;
|
|
||||||
|
|
||||||
public Range(long start, long end) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException(start, 0, end);
|
|
||||||
}
|
|
||||||
this.start = start;
|
|
||||||
this.end = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStart() {
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getEnd() {
|
|
||||||
return end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Range range = (Range) o;
|
|
||||||
return start == range.start && end == range.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", Range.class.getSimpleName() + "[", "]").add("start=" + start).add("end=" + end)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
|
||||||
@Override
|
|
||||||
public Range clone() {
|
|
||||||
return new Range(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnmodifiableRange unmodifiableClone() {
|
|
||||||
return new UnmodifiableRange(start, end);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package org.warp.commonutils.range;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSortedSet;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSortedSets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
|
||||||
|
|
||||||
public class Ranges {
|
|
||||||
|
|
||||||
private final ObjectArrayList<Range> ranges;
|
|
||||||
|
|
||||||
public Ranges(long start, long end) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException(start, 0, end);
|
|
||||||
}
|
|
||||||
this.ranges = new ObjectArrayList<>();
|
|
||||||
ranges.add(new Range(start, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRange(Range range) {
|
|
||||||
addRange(range.start, range.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRange(long start, long end) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException(start, 0, end);
|
|
||||||
}
|
|
||||||
long rangeStart = start;
|
|
||||||
long rangeEnd = end;
|
|
||||||
var it = ranges.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Range range = it.next();
|
|
||||||
if (range.start <= end && range.end >= start) {
|
|
||||||
boolean remove = false;
|
|
||||||
if (range.start < rangeStart && range.end >= rangeStart) {
|
|
||||||
rangeStart = range.start;
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
if (range.end > rangeEnd && range.start <= rangeEnd) {
|
|
||||||
rangeEnd = range.end;
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
if (remove) {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addRangeIfNotZero(new Range(rangeStart, rangeEnd));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteRange(final long start, final long end) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException(start);
|
|
||||||
}
|
|
||||||
List<Range> rangesToAdd = new ArrayList<>(ranges.size());
|
|
||||||
var it = ranges.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Range range = it.next();
|
|
||||||
if (range.start <= end && range.end >= start) {
|
|
||||||
if (range.start >= start && range.end <= end) {
|
|
||||||
// delete the range
|
|
||||||
it.remove();
|
|
||||||
} else if (range.start <= start && range.end >= end) {
|
|
||||||
// cut the hole
|
|
||||||
it.remove();
|
|
||||||
rangesToAdd.add(new Range(range.start, start));
|
|
||||||
rangesToAdd.add(new Range(end, range.end));
|
|
||||||
} else if (range.start <= start && range.end <= end && range.end > start) {
|
|
||||||
// shrink the right border
|
|
||||||
it.remove();
|
|
||||||
rangesToAdd.add(new Range(range.start, start));
|
|
||||||
} else if (range.start >= start && range.end >= end && range.start < end) {
|
|
||||||
// shrink the left border
|
|
||||||
it.remove();
|
|
||||||
rangesToAdd.add(new Range(end, range.end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Range rangeToAdd : rangesToAdd) {
|
|
||||||
addRangeIfNotZero(rangeToAdd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This methods does not check overlapping ranges! It's used only internally to skip empty ranges
|
|
||||||
*
|
|
||||||
* @param range
|
|
||||||
*/
|
|
||||||
private void addRangeIfNotZero(Range range) {
|
|
||||||
if (range.start != range.end) {
|
|
||||||
ranges.add(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectSortedSet<UnmodifiableRange> getRanges() {
|
|
||||||
ObjectSortedSet<UnmodifiableRange> a = new ObjectRBTreeSet<>(Comparator.comparingLong(UnmodifiableRange::getStart));
|
|
||||||
ranges.forEach((range) -> a.add(range.unmodifiableClone()));
|
|
||||||
return ObjectSortedSets.unmodifiable(a);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package org.warp.commonutils.range;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
|
||||||
|
|
||||||
public class UnmodifiableRange {
|
|
||||||
|
|
||||||
private final long start;
|
|
||||||
private final long end;
|
|
||||||
|
|
||||||
public UnmodifiableRange(long start, long end) {
|
|
||||||
if (start > end) {
|
|
||||||
throw new IndexOutOfBoundsException(start, 0, end);
|
|
||||||
}
|
|
||||||
this.start = start;
|
|
||||||
this.end = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStart() {
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getEnd() {
|
|
||||||
return end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UnmodifiableRange that = (UnmodifiableRange) o;
|
|
||||||
return start == that.start && end == that.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", UnmodifiableRange.class.getSimpleName() + "[", "]").add("start=" + start)
|
|
||||||
.add("end=" + end).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Range toRange() {
|
|
||||||
return new Range(start, end);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package org.warp.commonutils.serialization;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
|
||||||
import java.io.DataOutput;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class UTFUtils {
|
|
||||||
public static void writeUTF(DataOutput out, String utf) throws IOException {
|
|
||||||
byte[] bytes = utf.getBytes(StandardCharsets.UTF_8);
|
|
||||||
out.writeInt(bytes.length);
|
|
||||||
out.write(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String readUTF(DataInput in) throws IOException {
|
|
||||||
int len = in.readInt();
|
|
||||||
byte[] data = new byte[len];
|
|
||||||
in.readFully(data, 0, len);
|
|
||||||
return new String(data, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keep only ascii alphanumeric letters
|
|
||||||
*/
|
|
||||||
public static String keepOnlyASCII(String nextString) {
|
|
||||||
char[] chars = nextString.toCharArray();
|
|
||||||
//noinspection UnusedAssignment
|
|
||||||
nextString = null;
|
|
||||||
int writeIndex = 0;
|
|
||||||
char c;
|
|
||||||
for (int checkIndex = 0; checkIndex < chars.length; checkIndex++) {
|
|
||||||
c = chars[checkIndex];
|
|
||||||
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
|
||||||
if (writeIndex != checkIndex) {
|
|
||||||
chars[writeIndex] = c;
|
|
||||||
}
|
|
||||||
writeIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new String(chars, 0, writeIndex);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple {@link InputStream} implementation that exposes currently
|
|
||||||
* available content of a {@link ByteBuffer}.
|
|
||||||
*/
|
|
||||||
public class ByteBufferBackedInputStream extends InputStream {
|
|
||||||
protected final ByteBuffer _b;
|
|
||||||
|
|
||||||
public ByteBufferBackedInputStream(ByteBuffer buf) { _b = buf; }
|
|
||||||
|
|
||||||
@Override public int available() { return _b.remaining(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException { return _b.hasRemaining() ? (_b.get() & 0xFF) : -1; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte @NotNull [] bytes, int off, int len) throws IOException {
|
|
||||||
if (!_b.hasRemaining()) return -1;
|
|
||||||
len = Math.min(len, _b.remaining());
|
|
||||||
_b.get(bytes, off, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
|
||||||
import java.io.DataOutput;
|
|
||||||
|
|
||||||
public interface DataInputOutput extends DataInput, DataOutput {
|
|
||||||
|
|
||||||
DataInput getIn();
|
|
||||||
|
|
||||||
DataOutput getOut();
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
|
||||||
import java.io.DataOutput;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class DataInputOutputImpl implements DataInputOutput {
|
|
||||||
|
|
||||||
private final DataInput in;
|
|
||||||
private final DataOutput out;
|
|
||||||
|
|
||||||
public DataInputOutputImpl(DataInput in, DataOutput out) {
|
|
||||||
this.in = in;
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataInput getIn() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataOutput getOut() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readFully(byte @NotNull [] bytes) throws IOException {
|
|
||||||
in.readFully(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
|
||||||
in.readFully(bytes, i, i1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int skipBytes(int i) throws IOException {
|
|
||||||
return in.skipBytes(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean readBoolean() throws IOException {
|
|
||||||
return in.readBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte readByte() throws IOException {
|
|
||||||
return in.readByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedByte() throws IOException {
|
|
||||||
return in.readUnsignedByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short readShort() throws IOException {
|
|
||||||
return in.readShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedShort() throws IOException {
|
|
||||||
return in.readUnsignedShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char readChar() throws IOException {
|
|
||||||
return in.readChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readInt() throws IOException {
|
|
||||||
return in.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long readLong() throws IOException {
|
|
||||||
return in.readLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float readFloat() throws IOException {
|
|
||||||
return in.readFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double readDouble() throws IOException {
|
|
||||||
return in.readDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String readLine() throws IOException {
|
|
||||||
return in.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String readUTF() throws IOException {
|
|
||||||
return in.readUTF();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int i) throws IOException {
|
|
||||||
out.write(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte @NotNull [] bytes) throws IOException {
|
|
||||||
out.write(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
|
||||||
out.write(bytes, i, i1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBoolean(boolean b) throws IOException {
|
|
||||||
out.writeBoolean(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeByte(int i) throws IOException {
|
|
||||||
out.writeByte(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeShort(int i) throws IOException {
|
|
||||||
out.writeShort(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeChar(int i) throws IOException {
|
|
||||||
out.writeChar(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeInt(int i) throws IOException {
|
|
||||||
out.writeInt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeLong(long l) throws IOException {
|
|
||||||
out.writeLong(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeFloat(float v) throws IOException {
|
|
||||||
out.writeFloat(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeDouble(double v) throws IOException {
|
|
||||||
out.writeDouble(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBytes(@NotNull String s) throws IOException {
|
|
||||||
out.writeBytes(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeChars(@NotNull String s) throws IOException {
|
|
||||||
out.writeChars(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeUTF(@NotNull String s) throws IOException {
|
|
||||||
out.writeUTF(s);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class DataInputOutputStream extends DataOutputStream implements DataInputOutput {
|
|
||||||
|
|
||||||
private final DataInputStream in;
|
|
||||||
|
|
||||||
public DataInputOutputStream(DataInputStream in, DataOutputStream out) {
|
|
||||||
super(out);
|
|
||||||
this.in = in;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataInputStream getIn() {
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataOutputStream getOut() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readFully(byte @NotNull [] bytes) throws IOException {
|
|
||||||
in.readFully(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
|
||||||
in.readFully(bytes, i, i1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int skipBytes(int i) throws IOException {
|
|
||||||
return in.skipBytes(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean readBoolean() throws IOException {
|
|
||||||
return in.readBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte readByte() throws IOException {
|
|
||||||
return in.readByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedByte() throws IOException {
|
|
||||||
return in.readUnsignedByte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short readShort() throws IOException {
|
|
||||||
return in.readShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedShort() throws IOException {
|
|
||||||
return in.readUnsignedShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char readChar() throws IOException {
|
|
||||||
return in.readChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readInt() throws IOException {
|
|
||||||
return in.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long readLong() throws IOException {
|
|
||||||
return in.readLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float readFloat() throws IOException {
|
|
||||||
return in.readFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double readDouble() throws IOException {
|
|
||||||
return in.readDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public String readLine() throws IOException {
|
|
||||||
return in.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String readUTF() throws IOException {
|
|
||||||
return in.readUTF();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,580 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A data input stream lets an application read primitive Java data
|
|
||||||
* types from an underlying input stream in a machine-independent
|
|
||||||
* way. An application uses a data output stream to write data that
|
|
||||||
* can later be read by a data input stream.
|
|
||||||
* <p>
|
|
||||||
* DataInputStream is not necessarily safe for multithreaded access.
|
|
||||||
* Thread safety is optional and is the responsibility of users of
|
|
||||||
* methods in this class.
|
|
||||||
*
|
|
||||||
* @author Arthur van Hoff
|
|
||||||
* @see java.io.DataOutputStream
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public class SafeDataInputStream extends SafeFilterInputStream implements DataInput {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DataInputStream that uses the specified
|
|
||||||
* underlying InputStream.
|
|
||||||
*
|
|
||||||
* @param in the specified input stream
|
|
||||||
*/
|
|
||||||
public SafeDataInputStream(SafeInputStream in) {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* working arrays initialized on demand by readUTF
|
|
||||||
*/
|
|
||||||
private byte[] bytearr = new byte[80];
|
|
||||||
private char[] chararr = new char[80];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads some number of bytes from the contained input stream and
|
|
||||||
* stores them into the buffer array {@code b}. The number of
|
|
||||||
* bytes actually read is returned as an integer. This method blocks
|
|
||||||
* until input data is available, end of file is detected, or an
|
|
||||||
* exception is thrown.
|
|
||||||
*
|
|
||||||
* <p>If {@code b} is null, a {@code NullPointerException} is
|
|
||||||
* thrown. If the length of {@code b} is zero, then no bytes are
|
|
||||||
* read and {@code 0} is returned; otherwise, there is an attempt
|
|
||||||
* to read at least one byte. If no byte is available because the
|
|
||||||
* stream is at end of file, the value {@code -1} is returned;
|
|
||||||
* otherwise, at least one byte is read and stored into {@code b}.
|
|
||||||
*
|
|
||||||
* <p>The first byte read is stored into element {@code b[0]}, the
|
|
||||||
* next one into {@code b[1]}, and so on. The number of bytes read
|
|
||||||
* is, at most, equal to the length of {@code b}. Let {@code k}
|
|
||||||
* be the number of bytes actually read; these bytes will be stored in
|
|
||||||
* elements {@code b[0]} through {@code b[k-1]}, leaving
|
|
||||||
* elements {@code b[k]} through {@code b[b.length-1]}
|
|
||||||
* unaffected.
|
|
||||||
*
|
|
||||||
* <p>The {@code read(b)} method has the same effect as:
|
|
||||||
* <blockquote><pre>
|
|
||||||
* read(b, 0, b.length)
|
|
||||||
* </pre></blockquote>
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @return the total number of bytes read into the buffer, or
|
|
||||||
* {@code -1} if there is no more data because the end
|
|
||||||
* of the stream has been reached.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see java.io.InputStream#read(byte[], int, int)
|
|
||||||
*/
|
|
||||||
public final int read(byte[] b) {
|
|
||||||
return in.read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to {@code len} bytes of data from the contained
|
|
||||||
* input stream into an array of bytes. An attempt is made to read
|
|
||||||
* as many as {@code len} bytes, but a smaller number may be read,
|
|
||||||
* possibly zero. The number of bytes actually read is returned as an
|
|
||||||
* integer.
|
|
||||||
*
|
|
||||||
* <p> This method blocks until input data is available, end of file is
|
|
||||||
* detected, or an exception is thrown.
|
|
||||||
*
|
|
||||||
* <p> If {@code len} is zero, then no bytes are read and
|
|
||||||
* {@code 0} is returned; otherwise, there is an attempt to read at
|
|
||||||
* least one byte. If no byte is available because the stream is at end of
|
|
||||||
* file, the value {@code -1} is returned; otherwise, at least one
|
|
||||||
* byte is read and stored into {@code b}.
|
|
||||||
*
|
|
||||||
* <p> The first byte read is stored into element {@code b[off]}, the
|
|
||||||
* next one into {@code b[off+1]}, and so on. The number of bytes read
|
|
||||||
* is, at most, equal to {@code len}. Let <i>k</i> be the number of
|
|
||||||
* bytes actually read; these bytes will be stored in elements
|
|
||||||
* {@code b[off]} through {@code b[off+}<i>k</i>{@code -1]},
|
|
||||||
* leaving elements {@code b[off+}<i>k</i>{@code ]} through
|
|
||||||
* {@code b[off+len-1]} unaffected.
|
|
||||||
*
|
|
||||||
* <p> In every case, elements {@code b[0]} through
|
|
||||||
* {@code b[off]} and elements {@code b[off+len]} through
|
|
||||||
* {@code b[b.length-1]} are unaffected.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @param off the start offset in the destination array {@code b}
|
|
||||||
* @param len the maximum number of bytes read.
|
|
||||||
* @return the total number of bytes read into the buffer, or
|
|
||||||
* {@code -1} if there is no more data because the end
|
|
||||||
* of the stream has been reached.
|
|
||||||
* @throws NullPointerException If {@code b} is {@code null}.
|
|
||||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
|
||||||
* {@code len} is negative, or {@code len} is greater than
|
|
||||||
* {@code b.length - off}
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see java.io.InputStream#read(byte[], int, int)
|
|
||||||
*/
|
|
||||||
public final int read(byte[] b, int off, int len) {
|
|
||||||
return in.read(b, off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readFully}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @throws NullPointerException if {@code b} is {@code null}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final void readFully(byte @NotNull [] b) {
|
|
||||||
readFully(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readFully}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @param off the start offset in the data array {@code b}.
|
|
||||||
* @param len the number of bytes to read.
|
|
||||||
* @throws NullPointerException if {@code b} is {@code null}.
|
|
||||||
* @throws IndexOutOfBoundsException if {@code off} is negative,
|
|
||||||
* {@code len} is negative, or {@code len} is greater than
|
|
||||||
* {@code b.length - off}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final void readFully(byte @NotNull [] b, int off, int len) {
|
|
||||||
if (len < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
int n = 0;
|
|
||||||
while (n < len) {
|
|
||||||
int count = in.read(b, off + n, len - n);
|
|
||||||
if (count < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
n += count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code skipBytes}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @param n the number of bytes to be skipped.
|
|
||||||
* @return the actual number of bytes skipped.
|
|
||||||
*/
|
|
||||||
public final int skipBytes(int n) {
|
|
||||||
int total = 0;
|
|
||||||
int cur;
|
|
||||||
|
|
||||||
while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
|
|
||||||
total += cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readBoolean}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the {@code boolean} value read.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final boolean readBoolean() {
|
|
||||||
int ch = in.read();
|
|
||||||
if (ch < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return (ch != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readByte}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next byte of this input stream as a signed 8-bit
|
|
||||||
* {@code byte}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final byte readByte() {
|
|
||||||
int ch = in.read();
|
|
||||||
if (ch < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return (byte)(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readUnsignedByte}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next byte of this input stream, interpreted as an
|
|
||||||
* unsigned 8-bit number.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final int readUnsignedByte() {
|
|
||||||
int ch = in.read();
|
|
||||||
if (ch < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readShort}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next two bytes of this input stream, interpreted as a
|
|
||||||
* signed 16-bit number.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final short readShort() {
|
|
||||||
int ch1 = in.read();
|
|
||||||
int ch2 = in.read();
|
|
||||||
if ((ch1 | ch2) < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return (short)((ch1 << 8) + (ch2 << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readUnsignedShort}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next two bytes of this input stream, interpreted as an
|
|
||||||
* unsigned 16-bit integer.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final int readUnsignedShort() {
|
|
||||||
int ch1 = in.read();
|
|
||||||
int ch2 = in.read();
|
|
||||||
if ((ch1 | ch2) < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return (ch1 << 8) + (ch2 << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readChar}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next two bytes of this input stream, interpreted as a
|
|
||||||
* {@code char}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final char readChar() {
|
|
||||||
int ch1 = in.read();
|
|
||||||
int ch2 = in.read();
|
|
||||||
if ((ch1 | ch2) < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return (char)((ch1 << 8) + (ch2 << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readInt}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next four bytes of this input stream, interpreted as an
|
|
||||||
* {@code int}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final int readInt() {
|
|
||||||
int ch1 = in.read();
|
|
||||||
int ch2 = in.read();
|
|
||||||
int ch3 = in.read();
|
|
||||||
int ch4 = in.read();
|
|
||||||
if ((ch1 | ch2 | ch3 | ch4) < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private final byte[] readBuffer = new byte[8];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readLong}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next eight bytes of this input stream, interpreted as a
|
|
||||||
* {@code long}.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public final long readLong() {
|
|
||||||
readFully(readBuffer, 0, 8);
|
|
||||||
return (((long)readBuffer[0] << 56) +
|
|
||||||
((long)(readBuffer[1] & 255) << 48) +
|
|
||||||
((long)(readBuffer[2] & 255) << 40) +
|
|
||||||
((long)(readBuffer[3] & 255) << 32) +
|
|
||||||
((long)(readBuffer[4] & 255) << 24) +
|
|
||||||
((readBuffer[5] & 255) << 16) +
|
|
||||||
((readBuffer[6] & 255) << 8) +
|
|
||||||
((readBuffer[7] & 255) << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readFloat}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next four bytes of this input stream, interpreted as a
|
|
||||||
* {@code float}.
|
|
||||||
* @see SafeDataInputStream#readInt()
|
|
||||||
* @see java.lang.Float#intBitsToFloat(int)
|
|
||||||
*/
|
|
||||||
public final float readFloat() {
|
|
||||||
return Float.intBitsToFloat(readInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readDouble}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return the next eight bytes of this input stream, interpreted as a
|
|
||||||
* {@code double}.
|
|
||||||
* @see SafeDataInputStream#readLong()
|
|
||||||
* @see java.lang.Double#longBitsToDouble(long)
|
|
||||||
*/
|
|
||||||
public final double readDouble() {
|
|
||||||
return Double.longBitsToDouble(readLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
private char[] lineBuffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readLine}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @deprecated This method does not properly convert bytes to characters.
|
|
||||||
* As of JDK 1.1, the preferred way to read lines of text is via the
|
|
||||||
* {@code BufferedReader.readLine()} method. Programs that use the
|
|
||||||
* {@code DataInputStream} class to read lines can be converted to use
|
|
||||||
* the {@code BufferedReader} class by replacing code of the form:
|
|
||||||
* <blockquote><pre>
|
|
||||||
* DataInputStream d = new DataInputStream(in);
|
|
||||||
* </pre></blockquote>
|
|
||||||
* with:
|
|
||||||
* <blockquote><pre>
|
|
||||||
* BufferedReader d
|
|
||||||
* = new BufferedReader(new InputStreamReader(in));
|
|
||||||
* </pre></blockquote>
|
|
||||||
*
|
|
||||||
* @return the next line of text from this input stream.
|
|
||||||
* @see java.io.BufferedReader#readLine()
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public final String readLine() {
|
|
||||||
char[] buf = lineBuffer;
|
|
||||||
|
|
||||||
if (buf == null) {
|
|
||||||
buf = lineBuffer = new char[128];
|
|
||||||
}
|
|
||||||
|
|
||||||
int room = buf.length;
|
|
||||||
int offset = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
loop: while (true) {
|
|
||||||
switch (c = in.read()) {
|
|
||||||
case -1:
|
|
||||||
case '\n':
|
|
||||||
break loop;
|
|
||||||
|
|
||||||
case '\r':
|
|
||||||
int c2 = in.read();
|
|
||||||
if ((c2 != '\n') && (c2 != -1)) {
|
|
||||||
if (!(in instanceof SafePushbackInputStream)) {
|
|
||||||
this.in = new SafePushbackInputStream(in);
|
|
||||||
}
|
|
||||||
((SafePushbackInputStream)in).unread(c2);
|
|
||||||
}
|
|
||||||
break loop;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (--room < 0) {
|
|
||||||
buf = new char[offset + 128];
|
|
||||||
room = buf.length - offset - 1;
|
|
||||||
System.arraycopy(lineBuffer, 0, buf, 0, offset);
|
|
||||||
lineBuffer = buf;
|
|
||||||
}
|
|
||||||
buf[offset++] = (char) c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((c == -1) && (offset == 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return String.copyValueOf(buf, 0, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See the general contract of the {@code readUTF}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
* <p>
|
|
||||||
* Bytes
|
|
||||||
* for this operation are read from the contained
|
|
||||||
* input stream.
|
|
||||||
*
|
|
||||||
* @return a Unicode string.
|
|
||||||
* @see SafeDataInputStream#readUTF(SafeDataInputStream)
|
|
||||||
*/
|
|
||||||
public final @NotNull String readUTF() {
|
|
||||||
return readUTF(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads from the
|
|
||||||
* stream {@code in} a representation
|
|
||||||
* of a Unicode character string encoded in
|
|
||||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a> format;
|
|
||||||
* this string of characters is then returned as a {@code String}.
|
|
||||||
* The details of the modified UTF-8 representation
|
|
||||||
* are exactly the same as for the {@code readUTF}
|
|
||||||
* method of {@code DataInput}.
|
|
||||||
*
|
|
||||||
* @param in a data input stream.
|
|
||||||
* @return a Unicode string.
|
|
||||||
* @see SafeDataInputStream#readUnsignedShort()
|
|
||||||
*/
|
|
||||||
public static String readUTF(SafeDataInputStream in) {
|
|
||||||
int utflen = in.readUnsignedShort();
|
|
||||||
byte[] bytearr;
|
|
||||||
char[] chararr;
|
|
||||||
if (in.bytearr.length < utflen){
|
|
||||||
in.bytearr = new byte[utflen*2];
|
|
||||||
in.chararr = new char[utflen*2];
|
|
||||||
}
|
|
||||||
chararr = in.chararr;
|
|
||||||
bytearr = in.bytearr;
|
|
||||||
|
|
||||||
int c, char2, char3;
|
|
||||||
int count = 0;
|
|
||||||
int chararr_count=0;
|
|
||||||
|
|
||||||
in.readFully(bytearr, 0, utflen);
|
|
||||||
|
|
||||||
while (count < utflen) {
|
|
||||||
c = (int) bytearr[count] & 0xff;
|
|
||||||
if (c > 127) break;
|
|
||||||
count++;
|
|
||||||
chararr[chararr_count++]=(char)c;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (count < utflen) {
|
|
||||||
c = (int) bytearr[count] & 0xff;
|
|
||||||
switch (c >> 4) {
|
|
||||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
|
||||||
/* 0xxxxxxx*/
|
|
||||||
count++;
|
|
||||||
chararr[chararr_count++]=(char)c;
|
|
||||||
break;
|
|
||||||
case 12: case 13:
|
|
||||||
/* 110x xxxx 10xx xxxx*/
|
|
||||||
count += 2;
|
|
||||||
if (count > utflen)
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"malformed input: partial character at end");
|
|
||||||
char2 = bytearr[count-1];
|
|
||||||
if ((char2 & 0xC0) != 0x80)
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"malformed input around byte " + count);
|
|
||||||
chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
|
|
||||||
(char2 & 0x3F));
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
/* 1110 xxxx 10xx xxxx 10xx xxxx */
|
|
||||||
count += 3;
|
|
||||||
if (count > utflen)
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"malformed input: partial character at end");
|
|
||||||
char2 = bytearr[count-2];
|
|
||||||
char3 = bytearr[count-1];
|
|
||||||
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"malformed input around byte " + (count-1));
|
|
||||||
chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
|
|
||||||
((char2 & 0x3F) << 6) |
|
|
||||||
((char3 & 0x3F) << 0));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* 10xx xxxx, 1111 xxxx */
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"malformed input around byte " + count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The number of chars produced may be less than utflen
|
|
||||||
return new String(chararr, 0, chararr_count);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,339 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@code SafeDataOutput} interface provides
|
|
||||||
* for converting data from any of the Java
|
|
||||||
* primitive types to a series of bytes and
|
|
||||||
* writing these bytes to a binary stream.
|
|
||||||
* There is also a facility for converting
|
|
||||||
* a {@code String} into
|
|
||||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
|
||||||
* format and writing the resulting series
|
|
||||||
* of bytes.
|
|
||||||
* <p>
|
|
||||||
* For all the methods in this interface that
|
|
||||||
* write bytes, it is generally true that if
|
|
||||||
* a byte cannot be written for any reason,
|
|
||||||
* an {@code IOException} is thrown.
|
|
||||||
*
|
|
||||||
* @author Frank Yellin
|
|
||||||
* @see java.io.DataInput
|
|
||||||
* @see java.io.DataOutputStream
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public interface SafeDataOutput {
|
|
||||||
/**
|
|
||||||
* Writes to the output stream the eight
|
|
||||||
* low-order bits of the argument {@code b}.
|
|
||||||
* The 24 high-order bits of {@code b}
|
|
||||||
* are ignored.
|
|
||||||
*
|
|
||||||
* @param b the byte to be written.
|
|
||||||
*/
|
|
||||||
void write(int b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes to the output stream all the bytes in array {@code b}.
|
|
||||||
* If {@code b} is {@code null},
|
|
||||||
* a {@code NullPointerException} is thrown.
|
|
||||||
* If {@code b.length} is zero, then
|
|
||||||
* no bytes are written. Otherwise, the byte
|
|
||||||
* {@code b[0]} is written first, then
|
|
||||||
* {@code b[1]}, and so on; the last byte
|
|
||||||
* written is {@code b[b.length-1]}.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
*/
|
|
||||||
void write(byte b[]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code len} bytes from array
|
|
||||||
* {@code b}, in order, to
|
|
||||||
* the output stream. If {@code b}
|
|
||||||
* is {@code null}, a {@code NullPointerException}
|
|
||||||
* is thrown. If {@code off} is negative,
|
|
||||||
* or {@code len} is negative, or {@code off+len}
|
|
||||||
* is greater than the length of the array
|
|
||||||
* {@code b}, then an {@code IndexOutOfBoundsException}
|
|
||||||
* is thrown. If {@code len} is zero,
|
|
||||||
* then no bytes are written. Otherwise, the
|
|
||||||
* byte {@code b[off]} is written first,
|
|
||||||
* then {@code b[off+1]}, and so on; the
|
|
||||||
* last byte written is {@code b[off+len-1]}.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
* @param off the start offset in the data.
|
|
||||||
* @param len the number of bytes to write.
|
|
||||||
*/
|
|
||||||
void write(byte b[], int off, int len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code boolean} value to this output stream.
|
|
||||||
* If the argument {@code v}
|
|
||||||
* is {@code true}, the value {@code (byte)1}
|
|
||||||
* is written; if {@code v} is {@code false},
|
|
||||||
* the value {@code (byte)0} is written.
|
|
||||||
* The byte written by this method may
|
|
||||||
* be read by the {@code readBoolean}
|
|
||||||
* method of interface {@code DataInput},
|
|
||||||
* which will then return a {@code boolean}
|
|
||||||
* equal to {@code v}.
|
|
||||||
*
|
|
||||||
* @param v the boolean to be written.
|
|
||||||
*/
|
|
||||||
void writeBoolean(boolean v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes to the output stream the eight low-order
|
|
||||||
* bits of the argument {@code v}.
|
|
||||||
* The 24 high-order bits of {@code v}
|
|
||||||
* are ignored. (This means that {@code writeByte}
|
|
||||||
* does exactly the same thing as {@code write}
|
|
||||||
* for an integer argument.) The byte written
|
|
||||||
* by this method may be read by the {@code readByte}
|
|
||||||
* method of interface {@code DataInput},
|
|
||||||
* which will then return a {@code byte}
|
|
||||||
* equal to {@code (byte)v}.
|
|
||||||
*
|
|
||||||
* @param v the byte value to be written.
|
|
||||||
*/
|
|
||||||
void writeByte(int v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes two bytes to the output
|
|
||||||
* stream to represent the value of the argument.
|
|
||||||
* The byte values to be written, in the order
|
|
||||||
* shown, are:
|
|
||||||
* <pre>{@code
|
|
||||||
* (byte)(0xff & (v >> 8))
|
|
||||||
* (byte)(0xff & v)
|
|
||||||
* }</pre> <p>
|
|
||||||
* The bytes written by this method may be
|
|
||||||
* read by the {@code readShort} method
|
|
||||||
* of interface {@code DataInput}, which
|
|
||||||
* will then return a {@code short} equal
|
|
||||||
* to {@code (short)v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code short} value to be written.
|
|
||||||
*/
|
|
||||||
void writeShort(int v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code char} value, which
|
|
||||||
* is comprised of two bytes, to the
|
|
||||||
* output stream.
|
|
||||||
* The byte values to be written, in the order
|
|
||||||
* shown, are:
|
|
||||||
* <pre>{@code
|
|
||||||
* (byte)(0xff & (v >> 8))
|
|
||||||
* (byte)(0xff & v)
|
|
||||||
* }</pre><p>
|
|
||||||
* The bytes written by this method may be
|
|
||||||
* read by the {@code readChar} method
|
|
||||||
* of interface {@code DataInput}, which
|
|
||||||
* will then return a {@code char} equal
|
|
||||||
* to {@code (char)v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code char} value to be written.
|
|
||||||
*/
|
|
||||||
void writeChar(int v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an {@code int} value, which is
|
|
||||||
* comprised of four bytes, to the output stream.
|
|
||||||
* The byte values to be written, in the order
|
|
||||||
* shown, are:
|
|
||||||
* <pre>{@code
|
|
||||||
* (byte)(0xff & (v >> 24))
|
|
||||||
* (byte)(0xff & (v >> 16))
|
|
||||||
* (byte)(0xff & (v >> 8))
|
|
||||||
* (byte)(0xff & v)
|
|
||||||
* }</pre><p>
|
|
||||||
* The bytes written by this method may be read
|
|
||||||
* by the {@code readInt} method of interface
|
|
||||||
* {@code DataInput}, which will then
|
|
||||||
* return an {@code int} equal to {@code v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code int} value to be written.
|
|
||||||
*/
|
|
||||||
void writeInt(int v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code long} value, which is
|
|
||||||
* comprised of eight bytes, to the output stream.
|
|
||||||
* The byte values to be written, in the order
|
|
||||||
* shown, are:
|
|
||||||
* <pre>{@code
|
|
||||||
* (byte)(0xff & (v >> 56))
|
|
||||||
* (byte)(0xff & (v >> 48))
|
|
||||||
* (byte)(0xff & (v >> 40))
|
|
||||||
* (byte)(0xff & (v >> 32))
|
|
||||||
* (byte)(0xff & (v >> 24))
|
|
||||||
* (byte)(0xff & (v >> 16))
|
|
||||||
* (byte)(0xff & (v >> 8))
|
|
||||||
* (byte)(0xff & v)
|
|
||||||
* }</pre><p>
|
|
||||||
* The bytes written by this method may be
|
|
||||||
* read by the {@code readLong} method
|
|
||||||
* of interface {@code DataInput}, which
|
|
||||||
* will then return a {@code long} equal
|
|
||||||
* to {@code v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code long} value to be written.
|
|
||||||
*/
|
|
||||||
void writeLong(long v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code float} value,
|
|
||||||
* which is comprised of four bytes, to the output stream.
|
|
||||||
* It does this as if it first converts this
|
|
||||||
* {@code float} value to an {@code int}
|
|
||||||
* in exactly the manner of the {@code Float.floatToIntBits}
|
|
||||||
* method and then writes the {@code int}
|
|
||||||
* value in exactly the manner of the {@code writeInt}
|
|
||||||
* method. The bytes written by this method
|
|
||||||
* may be read by the {@code readFloat}
|
|
||||||
* method of interface {@code DataInput},
|
|
||||||
* which will then return a {@code float}
|
|
||||||
* equal to {@code v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code float} value to be written.
|
|
||||||
*/
|
|
||||||
void writeFloat(float v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code double} value,
|
|
||||||
* which is comprised of eight bytes, to the output stream.
|
|
||||||
* It does this as if it first converts this
|
|
||||||
* {@code double} value to a {@code long}
|
|
||||||
* in exactly the manner of the {@code Double.doubleToLongBits}
|
|
||||||
* method and then writes the {@code long}
|
|
||||||
* value in exactly the manner of the {@code writeLong}
|
|
||||||
* method. The bytes written by this method
|
|
||||||
* may be read by the {@code readDouble}
|
|
||||||
* method of interface {@code DataInput},
|
|
||||||
* which will then return a {@code double}
|
|
||||||
* equal to {@code v}.
|
|
||||||
*
|
|
||||||
* @param v the {@code double} value to be written.
|
|
||||||
*/
|
|
||||||
void writeDouble(double v);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to the output stream.
|
|
||||||
* For every character in the string
|
|
||||||
* {@code s}, taken in order, one byte
|
|
||||||
* is written to the output stream. If
|
|
||||||
* {@code s} is {@code null}, a {@code NullPointerException}
|
|
||||||
* is thrown.<p> If {@code s.length}
|
|
||||||
* is zero, then no bytes are written. Otherwise,
|
|
||||||
* the character {@code s[0]} is written
|
|
||||||
* first, then {@code s[1]}, and so on;
|
|
||||||
* the last character written is {@code s[s.length-1]}.
|
|
||||||
* For each character, one byte is written,
|
|
||||||
* the low-order byte, in exactly the manner
|
|
||||||
* of the {@code writeByte} method . The
|
|
||||||
* high-order eight bits of each character
|
|
||||||
* in the string are ignored.
|
|
||||||
*
|
|
||||||
* @param s the string of bytes to be written.
|
|
||||||
*/
|
|
||||||
void writeBytes(String s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes every character in the string {@code s},
|
|
||||||
* to the output stream, in order,
|
|
||||||
* two bytes per character. If {@code s}
|
|
||||||
* is {@code null}, a {@code NullPointerException}
|
|
||||||
* is thrown. If {@code s.length}
|
|
||||||
* is zero, then no characters are written.
|
|
||||||
* Otherwise, the character {@code s[0]}
|
|
||||||
* is written first, then {@code s[1]},
|
|
||||||
* and so on; the last character written is
|
|
||||||
* {@code s[s.length-1]}. For each character,
|
|
||||||
* two bytes are actually written, high-order
|
|
||||||
* byte first, in exactly the manner of the
|
|
||||||
* {@code writeChar} method.
|
|
||||||
*
|
|
||||||
* @param s the string value to be written.
|
|
||||||
*/
|
|
||||||
void writeChars(String s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes two bytes of length information
|
|
||||||
* to the output stream, followed
|
|
||||||
* by the
|
|
||||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
|
||||||
* representation
|
|
||||||
* of every character in the string {@code s}.
|
|
||||||
* If {@code s} is {@code null},
|
|
||||||
* a {@code NullPointerException} is thrown.
|
|
||||||
* Each character in the string {@code s}
|
|
||||||
* is converted to a group of one, two, or
|
|
||||||
* three bytes, depending on the value of the
|
|
||||||
* character.<p>
|
|
||||||
* If a character {@code c}
|
|
||||||
* is in the range <code>\u0001</code> through
|
|
||||||
* <code>\u007f</code>, it is represented
|
|
||||||
* by one byte:
|
|
||||||
* <pre>(byte)c </pre> <p>
|
|
||||||
* If a character {@code c} is <code>\u0000</code>
|
|
||||||
* or is in the range <code>\u0080</code>
|
|
||||||
* through <code>\u07ff</code>, then it is
|
|
||||||
* represented by two bytes, to be written
|
|
||||||
* in the order shown: <pre>{@code
|
|
||||||
* (byte)(0xc0 | (0x1f & (c >> 6)))
|
|
||||||
* (byte)(0x80 | (0x3f & c))
|
|
||||||
* }</pre> <p> If a character
|
|
||||||
* {@code c} is in the range <code>\u0800</code>
|
|
||||||
* through {@code uffff}, then it is
|
|
||||||
* represented by three bytes, to be written
|
|
||||||
* in the order shown: <pre>{@code
|
|
||||||
* (byte)(0xe0 | (0x0f & (c >> 12)))
|
|
||||||
* (byte)(0x80 | (0x3f & (c >> 6)))
|
|
||||||
* (byte)(0x80 | (0x3f & c))
|
|
||||||
* }</pre> <p> First,
|
|
||||||
* the total number of bytes needed to represent
|
|
||||||
* all the characters of {@code s} is
|
|
||||||
* calculated. If this number is larger than
|
|
||||||
* {@code 65535}, then a {@code UTFDataFormatException}
|
|
||||||
* is thrown. Otherwise, this length is written
|
|
||||||
* to the output stream in exactly the manner
|
|
||||||
* of the {@code writeShort} method;
|
|
||||||
* after this, the one-, two-, or three-byte
|
|
||||||
* representation of each character in the
|
|
||||||
* string {@code s} is written.<p> The
|
|
||||||
* bytes written by this method may be read
|
|
||||||
* by the {@code readUTF} method of interface
|
|
||||||
* {@code DataInput}, which will then
|
|
||||||
* return a {@code String} equal to {@code s}.
|
|
||||||
*
|
|
||||||
* @param s the string value to be written.
|
|
||||||
*/
|
|
||||||
void writeUTF(String s);
|
|
||||||
}
|
|
@ -1,408 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A data output stream lets an application write primitive Java data
|
|
||||||
* types to an output stream in a portable way. An application can
|
|
||||||
* then use a data input stream to read the data back in.
|
|
||||||
*
|
|
||||||
* @author unascribed
|
|
||||||
* @see java.io.DataInputStream
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public class SafeDataOutputStream extends SafeFilterOutputStream implements SafeDataOutput {
|
|
||||||
/**
|
|
||||||
* The number of bytes written to the data output stream so far.
|
|
||||||
* If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
|
||||||
*/
|
|
||||||
protected int written;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bytearr is initialized on demand by writeUTF
|
|
||||||
*/
|
|
||||||
private byte[] bytearr = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new data output stream to write data to the specified
|
|
||||||
* underlying output stream. The counter {@code written} is
|
|
||||||
* set to zero.
|
|
||||||
*
|
|
||||||
* @param out the underlying output stream, to be saved for later
|
|
||||||
* use.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public SafeDataOutputStream(SafeOutputStream out) {
|
|
||||||
super(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increases the written counter by the specified value
|
|
||||||
* until it reaches Integer.MAX_VALUE.
|
|
||||||
*/
|
|
||||||
private void incCount(int value) {
|
|
||||||
int temp = written + value;
|
|
||||||
if (temp < 0) {
|
|
||||||
temp = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
written = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the specified byte (the low eight bits of the argument
|
|
||||||
* {@code b}) to the underlying output stream. If no exception
|
|
||||||
* is thrown, the counter {@code written} is incremented by
|
|
||||||
* {@code 1}.
|
|
||||||
* <p>
|
|
||||||
* Implements the {@code write} method of {@code OutputStream}.
|
|
||||||
*
|
|
||||||
* @param b the {@code byte} to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public synchronized void write(int b) {
|
|
||||||
out.write(b);
|
|
||||||
incCount(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code len} bytes from the specified byte array
|
|
||||||
* starting at offset {@code off} to the underlying output stream.
|
|
||||||
* If no exception is thrown, the counter {@code written} is
|
|
||||||
* incremented by {@code len}.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
* @param off the start offset in the data.
|
|
||||||
* @param len the number of bytes to write.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public synchronized void write(byte b[], int off, int len)
|
|
||||||
{
|
|
||||||
out.write(b, off, len);
|
|
||||||
incCount(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flushes this data output stream. This forces any buffered output
|
|
||||||
* bytes to be written out to the stream.
|
|
||||||
* <p>
|
|
||||||
* The {@code flush} method of {@code SafeDataOutputStream}
|
|
||||||
* calls the {@code flush} method of its underlying output stream.
|
|
||||||
*
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
* @see java.io.OutputStream#flush()
|
|
||||||
*/
|
|
||||||
public void flush() {
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code boolean} to the underlying output stream as
|
|
||||||
* a 1-byte value. The value {@code true} is written out as the
|
|
||||||
* value {@code (byte)1}; the value {@code false} is
|
|
||||||
* written out as the value {@code (byte)0}. If no exception is
|
|
||||||
* thrown, the counter {@code written} is incremented by
|
|
||||||
* {@code 1}.
|
|
||||||
*
|
|
||||||
* @param v a {@code boolean} value to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeBoolean(boolean v) {
|
|
||||||
out.write(v ? 1 : 0);
|
|
||||||
incCount(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out a {@code byte} to the underlying output stream as
|
|
||||||
* a 1-byte value. If no exception is thrown, the counter
|
|
||||||
* {@code written} is incremented by {@code 1}.
|
|
||||||
*
|
|
||||||
* @param v a {@code byte} value to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeByte(int v) {
|
|
||||||
out.write(v);
|
|
||||||
incCount(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code short} to the underlying output stream as two
|
|
||||||
* bytes, high byte first. If no exception is thrown, the counter
|
|
||||||
* {@code written} is incremented by {@code 2}.
|
|
||||||
*
|
|
||||||
* @param v a {@code short} to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeShort(int v) {
|
|
||||||
out.write((v >>> 8) & 0xFF);
|
|
||||||
out.write((v >>> 0) & 0xFF);
|
|
||||||
incCount(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code char} to the underlying output stream as a
|
|
||||||
* 2-byte value, high byte first. If no exception is thrown, the
|
|
||||||
* counter {@code written} is incremented by {@code 2}.
|
|
||||||
*
|
|
||||||
* @param v a {@code char} value to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeChar(int v) {
|
|
||||||
out.write((v >>> 8) & 0xFF);
|
|
||||||
out.write((v >>> 0) & 0xFF);
|
|
||||||
incCount(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes an {@code int} to the underlying output stream as four
|
|
||||||
* bytes, high byte first. If no exception is thrown, the counter
|
|
||||||
* {@code written} is incremented by {@code 4}.
|
|
||||||
*
|
|
||||||
* @param v an {@code int} to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeInt(int v) {
|
|
||||||
out.write((v >>> 24) & 0xFF);
|
|
||||||
out.write((v >>> 16) & 0xFF);
|
|
||||||
out.write((v >>> 8) & 0xFF);
|
|
||||||
out.write((v >>> 0) & 0xFF);
|
|
||||||
incCount(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte writeBuffer[] = new byte[8];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a {@code long} to the underlying output stream as eight
|
|
||||||
* bytes, high byte first. In no exception is thrown, the counter
|
|
||||||
* {@code written} is incremented by {@code 8}.
|
|
||||||
*
|
|
||||||
* @param v a {@code long} to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeLong(long v) {
|
|
||||||
writeBuffer[0] = (byte)(v >>> 56);
|
|
||||||
writeBuffer[1] = (byte)(v >>> 48);
|
|
||||||
writeBuffer[2] = (byte)(v >>> 40);
|
|
||||||
writeBuffer[3] = (byte)(v >>> 32);
|
|
||||||
writeBuffer[4] = (byte)(v >>> 24);
|
|
||||||
writeBuffer[5] = (byte)(v >>> 16);
|
|
||||||
writeBuffer[6] = (byte)(v >>> 8);
|
|
||||||
writeBuffer[7] = (byte)(v >>> 0);
|
|
||||||
out.write(writeBuffer, 0, 8);
|
|
||||||
incCount(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the float argument to an {@code int} using the
|
|
||||||
* {@code floatToIntBits} method in class {@code Float},
|
|
||||||
* and then writes that {@code int} value to the underlying
|
|
||||||
* output stream as a 4-byte quantity, high byte first. If no
|
|
||||||
* exception is thrown, the counter {@code written} is
|
|
||||||
* incremented by {@code 4}.
|
|
||||||
*
|
|
||||||
* @param v a {@code float} value to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
* @see java.lang.Float#floatToIntBits(float)
|
|
||||||
*/
|
|
||||||
public final void writeFloat(float v) {
|
|
||||||
writeInt(Float.floatToIntBits(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the double argument to a {@code long} using the
|
|
||||||
* {@code doubleToLongBits} method in class {@code Double},
|
|
||||||
* and then writes that {@code long} value to the underlying
|
|
||||||
* output stream as an 8-byte quantity, high byte first. If no
|
|
||||||
* exception is thrown, the counter {@code written} is
|
|
||||||
* incremented by {@code 8}.
|
|
||||||
*
|
|
||||||
* @param v a {@code double} value to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
* @see java.lang.Double#doubleToLongBits(double)
|
|
||||||
*/
|
|
||||||
public final void writeDouble(double v) {
|
|
||||||
writeLong(Double.doubleToLongBits(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes out the string to the underlying output stream as a
|
|
||||||
* sequence of bytes. Each character in the string is written out, in
|
|
||||||
* sequence, by discarding its high eight bits. If no exception is
|
|
||||||
* thrown, the counter {@code written} is incremented by the
|
|
||||||
* length of {@code s}.
|
|
||||||
*
|
|
||||||
* @param s a string of bytes to be written.
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeBytes(String s) {
|
|
||||||
int len = s.length();
|
|
||||||
for (int i = 0 ; i < len ; i++) {
|
|
||||||
out.write((byte)s.charAt(i));
|
|
||||||
}
|
|
||||||
incCount(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to the underlying output stream as a sequence of
|
|
||||||
* characters. Each character is written to the data output stream as
|
|
||||||
* if by the {@code writeChar} method. If no exception is
|
|
||||||
* thrown, the counter {@code written} is incremented by twice
|
|
||||||
* the length of {@code s}.
|
|
||||||
*
|
|
||||||
* @param s a {@code String} value to be written.
|
|
||||||
* @see SafeDataOutputStream#writeChar(int)
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
public final void writeChars(String s) {
|
|
||||||
int len = s.length();
|
|
||||||
for (int i = 0 ; i < len ; i++) {
|
|
||||||
int v = s.charAt(i);
|
|
||||||
out.write((v >>> 8) & 0xFF);
|
|
||||||
out.write((v >>> 0) & 0xFF);
|
|
||||||
}
|
|
||||||
incCount(len * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to the underlying output stream using
|
|
||||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
|
||||||
* encoding in a machine-independent manner.
|
|
||||||
* <p>
|
|
||||||
* First, two bytes are written to the output stream as if by the
|
|
||||||
* {@code writeShort} method giving the number of bytes to
|
|
||||||
* follow. This value is the number of bytes actually written out,
|
|
||||||
* not the length of the string. Following the length, each character
|
|
||||||
* of the string is output, in sequence, using the modified UTF-8 encoding
|
|
||||||
* for the character. If no exception is thrown, the counter
|
|
||||||
* {@code written} is incremented by the total number of
|
|
||||||
* bytes written to the output stream. This will be at least two
|
|
||||||
* plus the length of {@code str}, and at most two plus
|
|
||||||
* thrice the length of {@code str}.
|
|
||||||
*
|
|
||||||
* @param str a string to be written.
|
|
||||||
* @see #writeChars(String)
|
|
||||||
*/
|
|
||||||
public final void writeUTF(String str) {
|
|
||||||
writeUTF(str, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to the specified DataOutput using
|
|
||||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
|
||||||
* encoding in a machine-independent manner.
|
|
||||||
* <p>
|
|
||||||
* First, two bytes are written to out as if by the {@code writeShort}
|
|
||||||
* method giving the number of bytes to follow. This value is the number of
|
|
||||||
* bytes actually written out, not the length of the string. Following the
|
|
||||||
* length, each character of the string is output, in sequence, using the
|
|
||||||
* modified UTF-8 encoding for the character. If no exception is thrown, the
|
|
||||||
* counter {@code written} is incremented by the total number of
|
|
||||||
* bytes written to the output stream. This will be at least two
|
|
||||||
* plus the length of {@code str}, and at most two plus
|
|
||||||
* thrice the length of {@code str}.
|
|
||||||
*
|
|
||||||
* @param str a string to be written.
|
|
||||||
* @param out destination to write to
|
|
||||||
* @return The number of bytes written out.
|
|
||||||
*/
|
|
||||||
static int writeUTF(String str, SafeDataOutput out) {
|
|
||||||
final int strlen = str.length();
|
|
||||||
int utflen = strlen; // optimized for ASCII
|
|
||||||
|
|
||||||
for (int i = 0; i < strlen; i++) {
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c >= 0x80 || c == 0)
|
|
||||||
utflen += (c >= 0x800) ? 2 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utflen > 65535 || /* overflow */ utflen < strlen)
|
|
||||||
throw new IllegalArgumentException(tooLongMsg(str, utflen));
|
|
||||||
|
|
||||||
final byte[] bytearr;
|
|
||||||
if (out instanceof SafeDataOutputStream) {
|
|
||||||
SafeDataOutputStream dos = (SafeDataOutputStream)out;
|
|
||||||
if (dos.bytearr == null || (dos.bytearr.length < (utflen + 2)))
|
|
||||||
dos.bytearr = new byte[(utflen*2) + 2];
|
|
||||||
bytearr = dos.bytearr;
|
|
||||||
} else {
|
|
||||||
bytearr = new byte[utflen + 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
|
|
||||||
bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c >= 0x80 || c == 0) break;
|
|
||||||
bytearr[count++] = (byte) c;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; i < strlen; i++) {
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c < 0x80 && c != 0) {
|
|
||||||
bytearr[count++] = (byte) c;
|
|
||||||
} else if (c >= 0x800) {
|
|
||||||
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
} else {
|
|
||||||
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.write(bytearr, 0, utflen + 2);
|
|
||||||
return utflen + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String tooLongMsg(String s, int bits32) {
|
|
||||||
int slen = s.length();
|
|
||||||
String head = s.substring(0, 8);
|
|
||||||
String tail = s.substring(slen - 8, slen);
|
|
||||||
// handle int overflow with max 3x expansion
|
|
||||||
long actualLength = (long)slen + Integer.toUnsignedLong(bits32 - slen);
|
|
||||||
return "encoded string (" + head + "..." + tail + ") too long: "
|
|
||||||
+ actualLength + " bytes";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current value of the counter {@code written},
|
|
||||||
* the number of bytes written to this data output stream so far.
|
|
||||||
* If the counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
|
||||||
*
|
|
||||||
* @return the value of the {@code written} field.
|
|
||||||
* @see SafeDataOutputStream#written
|
|
||||||
*/
|
|
||||||
public final int size() {
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataOutputStream asDataOutputStream() {
|
|
||||||
return new DataOutputStream(this.out);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,210 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@code FilterInputStream} contains
|
|
||||||
* some other input stream, which it uses as
|
|
||||||
* its basic source of data, possibly transforming
|
|
||||||
* the data along the way or providing additional
|
|
||||||
* functionality. The class {@code FilterInputStream}
|
|
||||||
* itself simply overrides all methods of
|
|
||||||
* {@code InputStream} with versions that
|
|
||||||
* pass all requests to the contained input
|
|
||||||
* stream. Subclasses of {@code FilterInputStream}
|
|
||||||
* may further override some of these methods
|
|
||||||
* and may also provide additional methods
|
|
||||||
* and fields.
|
|
||||||
*
|
|
||||||
* @author Jonathan Payne
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public class SafeFilterInputStream extends SafeInputStream {
|
|
||||||
/**
|
|
||||||
* The input stream to be filtered.
|
|
||||||
*/
|
|
||||||
protected volatile SafeInputStream in;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@code FilterInputStream}
|
|
||||||
* by assigning the argument {@code in}
|
|
||||||
* to the field {@code this.in} so as
|
|
||||||
* to remember it for later use.
|
|
||||||
*
|
|
||||||
* @param in the underlying input stream, or {@code null} if
|
|
||||||
* this instance is to be created without an underlying stream.
|
|
||||||
*/
|
|
||||||
protected SafeFilterInputStream(SafeInputStream in) {
|
|
||||||
this.in = in;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the next byte of data from this input stream. The value
|
|
||||||
* byte is returned as an {@code int} in the range
|
|
||||||
* {@code 0} to {@code 255}. If no byte is available
|
|
||||||
* because the end of the stream has been reached, the value
|
|
||||||
* {@code -1} is returned. This method blocks until input data
|
|
||||||
* is available, the end of the stream is detected, or an exception
|
|
||||||
* is thrown.
|
|
||||||
* <p>
|
|
||||||
* This method
|
|
||||||
* simply performs {@code in.read()} and returns the result.
|
|
||||||
*
|
|
||||||
* @return the next byte of data, or {@code -1} if the end of the
|
|
||||||
* stream is reached.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public int read() {
|
|
||||||
return in.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to {@code b.length} bytes of data from this
|
|
||||||
* input stream into an array of bytes. This method blocks until some
|
|
||||||
* input is available.
|
|
||||||
* <p>
|
|
||||||
* This method simply performs the call
|
|
||||||
* {@code read(b, 0, b.length)} and returns
|
|
||||||
* the result. It is important that it does
|
|
||||||
* <i>not</i> do {@code in.read(b)} instead;
|
|
||||||
* certain subclasses of {@code FilterInputStream}
|
|
||||||
* depend on the implementation strategy actually
|
|
||||||
* used.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @return the total number of bytes read into the buffer, or
|
|
||||||
* {@code -1} if there is no more data because the end of
|
|
||||||
* the stream has been reached.
|
|
||||||
* @see SafeFilterInputStream#read(byte[], int, int)
|
|
||||||
*/
|
|
||||||
public int read(byte b[]) {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to {@code len} bytes of data from this input stream
|
|
||||||
* into an array of bytes. If {@code len} is not zero, the method
|
|
||||||
* blocks until some input is available; otherwise, no
|
|
||||||
* bytes are read and {@code 0} is returned.
|
|
||||||
* <p>
|
|
||||||
* This method simply performs {@code in.read(b, off, len)}
|
|
||||||
* and returns the result.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @param off the start offset in the destination array {@code b}
|
|
||||||
* @param len the maximum number of bytes read.
|
|
||||||
* @return the total number of bytes read into the buffer, or
|
|
||||||
* {@code -1} if there is no more data because the end of
|
|
||||||
* the stream has been reached.
|
|
||||||
* @throws NullPointerException If {@code b} is {@code null}.
|
|
||||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
|
||||||
* {@code len} is negative, or {@code len} is greater than
|
|
||||||
* {@code b.length - off}
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public int read(byte b[], int off, int len) {
|
|
||||||
return in.read(b, off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips over and discards {@code n} bytes of data from the
|
|
||||||
* input stream. The {@code skip} method may, for a variety of
|
|
||||||
* reasons, end up skipping over some smaller number of bytes,
|
|
||||||
* possibly {@code 0}. The actual number of bytes skipped is
|
|
||||||
* returned.
|
|
||||||
* <p>
|
|
||||||
* This method simply performs {@code in.skip(n)}.
|
|
||||||
*
|
|
||||||
* @param n the number of bytes to be skipped.
|
|
||||||
* @return the actual number of bytes skipped.
|
|
||||||
*/
|
|
||||||
public long skip(long n) {
|
|
||||||
return in.skip(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an estimate of the number of bytes that can be read (or
|
|
||||||
* skipped over) from this input stream without blocking by the next
|
|
||||||
* caller of a method for this input stream. The next caller might be
|
|
||||||
* the same thread or another thread. A single read or skip of this
|
|
||||||
* many bytes will not block, but may read or skip fewer bytes.
|
|
||||||
* <p>
|
|
||||||
* This method returns the result of {@link #in in}.available().
|
|
||||||
*
|
|
||||||
* @return an estimate of the number of bytes that can be read (or skipped
|
|
||||||
* over) from this input stream without blocking.
|
|
||||||
*/
|
|
||||||
public int available() {
|
|
||||||
return in.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this input stream and releases any system resources
|
|
||||||
* associated with the stream.
|
|
||||||
* This
|
|
||||||
* method simply performs {@code in.close()}.
|
|
||||||
*
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the current position in this input stream. A subsequent
|
|
||||||
* call to the {@code reset} method repositions this stream at
|
|
||||||
* the last marked position so that subsequent reads re-read the same bytes.
|
|
||||||
* <p>
|
|
||||||
* The {@code readlimit} argument tells this input stream to
|
|
||||||
* allow that many bytes to be read before the mark position gets
|
|
||||||
* invalidated.
|
|
||||||
* <p>
|
|
||||||
* This method simply performs {@code in.mark(readlimit)}.
|
|
||||||
*
|
|
||||||
* @param readlimit the maximum limit of bytes that can be read before
|
|
||||||
* the mark position becomes invalid.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see SafeFilterInputStream#reset()
|
|
||||||
*/
|
|
||||||
public synchronized void mark(int readlimit) {
|
|
||||||
in.mark(readlimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repositions this stream to the position at the time the
|
|
||||||
* {@code mark} method was last called on this input stream.
|
|
||||||
* <p>
|
|
||||||
* This method
|
|
||||||
* simply performs {@code in.reset()}.
|
|
||||||
* <p>
|
|
||||||
* Stream marks are intended to be used in
|
|
||||||
* situations where you need to read ahead a little to see what's in
|
|
||||||
* the stream. Often this is most easily done by invoking some
|
|
||||||
* general parser. If the stream is of the type handled by the
|
|
||||||
* parse, it just chugs along happily. If the stream is not of
|
|
||||||
* that type, the parser should toss an exception when it fails.
|
|
||||||
* If this happens within readlimit bytes, it allows the outer
|
|
||||||
* code to reset the stream and try another parser.
|
|
||||||
*
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see SafeFilterInputStream#mark(int)
|
|
||||||
*/
|
|
||||||
public synchronized void reset() {
|
|
||||||
in.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests if this input stream supports the {@code mark}
|
|
||||||
* and {@code reset} methods.
|
|
||||||
* This method
|
|
||||||
* simply performs {@code in.markSupported()}.
|
|
||||||
*
|
|
||||||
* @return {@code true} if this stream type supports the
|
|
||||||
* {@code mark} and {@code reset} method;
|
|
||||||
* {@code false} otherwise.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see java.io.InputStream#mark(int)
|
|
||||||
* @see java.io.InputStream#reset()
|
|
||||||
*/
|
|
||||||
public boolean markSupported() {
|
|
||||||
return in.markSupported();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is the superclass of all classes that filter output
|
|
||||||
* streams. These streams sit on top of an already existing output
|
|
||||||
* stream (the <i>underlying</i> output stream) which it uses as its
|
|
||||||
* basic sink of data, but possibly transforming the data along the
|
|
||||||
* way or providing additional functionality.
|
|
||||||
* <p>
|
|
||||||
* The class {@code FilterOutputStream} itself simply overrides
|
|
||||||
* all methods of {@code SafeOutputStream} with versions that pass
|
|
||||||
* all requests to the underlying output stream. Subclasses of
|
|
||||||
* {@code FilterOutputStream} may further override some of these
|
|
||||||
* methods as well as provide additional methods and fields.
|
|
||||||
*
|
|
||||||
* @author Jonathan Payne
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public class SafeFilterOutputStream extends SafeOutputStream {
|
|
||||||
/**
|
|
||||||
* The underlying output stream to be filtered.
|
|
||||||
*/
|
|
||||||
protected SafeOutputStream out;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the stream is closed; implicitly initialized to false.
|
|
||||||
*/
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object used to prevent a race on the 'closed' instance variable.
|
|
||||||
*/
|
|
||||||
private final Object closeLock = new Object();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an output stream filter built on top of the specified
|
|
||||||
* underlying output stream.
|
|
||||||
*
|
|
||||||
* @param out the underlying output stream to be assigned to
|
|
||||||
* the field {@code this.out} for later use, or
|
|
||||||
* {@code null} if this instance is to be
|
|
||||||
* created without an underlying stream.
|
|
||||||
*/
|
|
||||||
public SafeFilterOutputStream(SafeOutputStream out) {
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the specified {@code byte} to this output stream.
|
|
||||||
* <p>
|
|
||||||
* The {@code write} method of {@code FilterOutputStream}
|
|
||||||
* calls the {@code write} method of its underlying output stream,
|
|
||||||
* that is, it performs {@code out.write(b)}.
|
|
||||||
* <p>
|
|
||||||
* Implements the abstract {@code write} method of {@code SafeOutputStream}.
|
|
||||||
*
|
|
||||||
* @param b the {@code byte}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(int b) {
|
|
||||||
out.write(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code b.length} bytes to this output stream.
|
|
||||||
* <p>
|
|
||||||
* The {@code write} method of {@code FilterOutputStream}
|
|
||||||
* calls its {@code write} method of three arguments with the
|
|
||||||
* arguments {@code b}, {@code 0}, and
|
|
||||||
* {@code b.length}.
|
|
||||||
* <p>
|
|
||||||
* Note that this method does not call the one-argument
|
|
||||||
* {@code write} method of its underlying output stream with
|
|
||||||
* the single argument {@code b}.
|
|
||||||
*
|
|
||||||
* @param b the data to be written.
|
|
||||||
* @see SafeFilterOutputStream#write(byte[], int, int)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(byte b[]) {
|
|
||||||
write(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code len} bytes from the specified
|
|
||||||
* {@code byte} array starting at offset {@code off} to
|
|
||||||
* this output stream.
|
|
||||||
* <p>
|
|
||||||
* The {@code write} method of {@code FilterOutputStream}
|
|
||||||
* calls the {@code write} method of one argument on each
|
|
||||||
* {@code byte} to output.
|
|
||||||
* <p>
|
|
||||||
* Note that this method does not call the {@code write} method
|
|
||||||
* of its underlying output stream with the same arguments. Subclasses
|
|
||||||
* of {@code FilterOutputStream} should provide a more efficient
|
|
||||||
* implementation of this method.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
* @param off the start offset in the data.
|
|
||||||
* @param len the number of bytes to write.
|
|
||||||
* @see SafeFilterOutputStream#write(int)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(byte b[], int off, int len) {
|
|
||||||
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
|
|
||||||
for (int i = 0 ; i < len ; i++) {
|
|
||||||
write(b[off + i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flushes this output stream and forces any buffered output bytes
|
|
||||||
* to be written out to the stream.
|
|
||||||
* <p>
|
|
||||||
* The {@code flush} method of {@code FilterOutputStream}
|
|
||||||
* calls the {@code flush} method of its underlying output stream.
|
|
||||||
*
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this output stream and releases any system resources
|
|
||||||
* associated with the stream.
|
|
||||||
* <p>
|
|
||||||
* When not already closed, the {@code close} method of {@code
|
|
||||||
* FilterOutputStream} calls its {@code flush} method, and then
|
|
||||||
* calls the {@code close} method of its underlying output stream.
|
|
||||||
*
|
|
||||||
* @see SafeFilterOutputStream#flush()
|
|
||||||
* @see SafeFilterOutputStream#out
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (closed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (closeLock) {
|
|
||||||
if (closed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Throwable flushException = null;
|
|
||||||
try {
|
|
||||||
flush();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
flushException = e;
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
if (flushException == null) {
|
|
||||||
out.close();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
out.close();
|
|
||||||
} catch (Throwable closeException) {
|
|
||||||
// evaluate possible precedence of flushException over closeException
|
|
||||||
if ((flushException instanceof ThreadDeath) &&
|
|
||||||
!(closeException instanceof ThreadDeath)) {
|
|
||||||
flushException.addSuppressed(closeException);
|
|
||||||
throw (ThreadDeath) flushException;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flushException != closeException) {
|
|
||||||
closeException.addSuppressed(flushException);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw closeException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,201 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public abstract class SafeInputStream extends InputStream {
|
|
||||||
|
|
||||||
// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
|
|
||||||
// use when skipping.
|
|
||||||
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
|
|
||||||
|
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 8192;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract int read();
|
|
||||||
|
|
||||||
public int read(byte b[]) {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int read(byte b[], int off, int len) {
|
|
||||||
Objects.checkFromIndexSize(off, len, b.length);
|
|
||||||
if (len == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int c = read();
|
|
||||||
if (c == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
b[off] = (byte)c;
|
|
||||||
|
|
||||||
int i = 1;
|
|
||||||
for (; i < len ; i++) {
|
|
||||||
c = read();
|
|
||||||
if (c == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
b[off + i] = (byte)c;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] readAllBytes() {
|
|
||||||
return readNBytes(Integer.MAX_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
|
||||||
|
|
||||||
public byte[] readNBytes(int len) {
|
|
||||||
if (len < 0) {
|
|
||||||
throw new IllegalArgumentException("len < 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<byte[]> bufs = null;
|
|
||||||
byte[] result = null;
|
|
||||||
int total = 0;
|
|
||||||
int remaining = len;
|
|
||||||
int n;
|
|
||||||
do {
|
|
||||||
byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
|
|
||||||
int nread = 0;
|
|
||||||
|
|
||||||
// read to EOF which may read more or less than buffer size
|
|
||||||
while ((n = read(buf, nread,
|
|
||||||
Math.min(buf.length - nread, remaining))) > 0) {
|
|
||||||
nread += n;
|
|
||||||
remaining -= n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nread > 0) {
|
|
||||||
if (MAX_BUFFER_SIZE - total < nread) {
|
|
||||||
throw new OutOfMemoryError("Required array size too large");
|
|
||||||
}
|
|
||||||
total += nread;
|
|
||||||
if (result == null) {
|
|
||||||
result = buf;
|
|
||||||
} else {
|
|
||||||
if (bufs == null) {
|
|
||||||
bufs = new ArrayList<>();
|
|
||||||
bufs.add(result);
|
|
||||||
}
|
|
||||||
bufs.add(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if the last call to read returned -1 or the number of bytes
|
|
||||||
// requested have been read then break
|
|
||||||
} while (n >= 0 && remaining > 0);
|
|
||||||
|
|
||||||
if (bufs == null) {
|
|
||||||
if (result == null) {
|
|
||||||
return new byte[0];
|
|
||||||
}
|
|
||||||
return result.length == total ?
|
|
||||||
result : Arrays.copyOf(result, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new byte[total];
|
|
||||||
int offset = 0;
|
|
||||||
remaining = total;
|
|
||||||
for (byte[] b : bufs) {
|
|
||||||
int count = Math.min(b.length, remaining);
|
|
||||||
System.arraycopy(b, 0, result, offset, count);
|
|
||||||
offset += count;
|
|
||||||
remaining -= count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readNBytes(byte[] b, int off, int len) {
|
|
||||||
Objects.checkFromIndexSize(off, len, b.length);
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
while (n < len) {
|
|
||||||
int count = read(b, off + n, len - n);
|
|
||||||
if (count < 0)
|
|
||||||
break;
|
|
||||||
n += count;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long skip(long n) {
|
|
||||||
long remaining = n;
|
|
||||||
int nr;
|
|
||||||
|
|
||||||
if (n <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
|
|
||||||
byte[] skipBuffer = new byte[size];
|
|
||||||
while (remaining > 0) {
|
|
||||||
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
|
|
||||||
if (nr < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
remaining -= nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n - remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skipNBytes(long n) {
|
|
||||||
if (n > 0) {
|
|
||||||
long ns = skip(n);
|
|
||||||
if (ns >= 0 && ns < n) { // skipped too few bytes
|
|
||||||
// adjust number to skip
|
|
||||||
n -= ns;
|
|
||||||
// read until requested number skipped or EOS reached
|
|
||||||
while (n > 0 && read() != -1) {
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
// if not enough skipped, then EOFE
|
|
||||||
if (n != 0) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
} else if (ns != n) { // skipped negative or too many bytes
|
|
||||||
throw new IllegalArgumentException("Unable to skip exactly");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int available() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {}
|
|
||||||
|
|
||||||
public synchronized void mark(int readlimit) {}
|
|
||||||
|
|
||||||
public synchronized void reset() {
|
|
||||||
throw new UnsupportedOperationException("mark/reset not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean markSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long transferTo(OutputStream out) {
|
|
||||||
Objects.requireNonNull(out, "out");
|
|
||||||
long transferred = 0;
|
|
||||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
|
||||||
int read;
|
|
||||||
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
|
|
||||||
try {
|
|
||||||
out.write(buffer, 0, read);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
transferred += read;
|
|
||||||
}
|
|
||||||
return transferred;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/** An {@link InputStream} that implements also the {@link SafeMeasurableStream} interface.
|
|
||||||
*
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
|
|
||||||
public abstract class SafeMeasurableInputStream extends SafeInputStream implements SafeMeasurableStream {
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/** An {@link OutputStream} that implements also the {@link SafeMeasurableStream} interface.
|
|
||||||
*
|
|
||||||
* @since 6.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
public abstract class SafeMeasurableOutputStream extends SafeOutputStream implements SafeMeasurableStream {
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/** An stream that provides eager access to its length,
|
|
||||||
* and keeps track of the current position (e.g., the number of bytes read so far, or the current
|
|
||||||
* position of the file pointer).
|
|
||||||
*
|
|
||||||
* <p>This class has two methods, both specified as optional. This apparently bizarre
|
|
||||||
* behaviour is necessary because of wrapper classes which use reflection
|
|
||||||
* to support those methods (see, e.g., {@link MeasurableInputStream}, {@link FastBufferedInputStream} and {@link FastBufferedOutputStream}).
|
|
||||||
*
|
|
||||||
* @since 6.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
public interface SafeMeasurableStream {
|
|
||||||
|
|
||||||
/** Returns the overall length of this stream (optional operation). In most cases, this will require the
|
|
||||||
* stream to perform some extra action, possibly changing the state of the input stream itself (typically, reading
|
|
||||||
* all the bytes up to the end, or flushing on output stream).
|
|
||||||
* Implementing classes should always document what state will the input stream be in
|
|
||||||
* after calling this method, and which kind of exception could be thrown.
|
|
||||||
*/
|
|
||||||
long length();
|
|
||||||
|
|
||||||
/** Returns the current position in this stream (optional operation).
|
|
||||||
*
|
|
||||||
* <p>Usually, the position is just the number of bytes read or written
|
|
||||||
* since the stream was opened, but in the case of a
|
|
||||||
* {@link it.unimi.dsi.fastutil.io.RepositionableStream} it
|
|
||||||
* represent the current position.
|
|
||||||
*/
|
|
||||||
long position();
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.Flushable;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Objects;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This abstract class is the superclass of all classes representing
|
|
||||||
* an output stream of bytes. An output stream accepts output bytes
|
|
||||||
* and sends them to some sink.
|
|
||||||
* <p>
|
|
||||||
* Applications that need to define a subclass of
|
|
||||||
* {@code OutputStream} must always provide at least a method
|
|
||||||
* that writes one byte of output.
|
|
||||||
*
|
|
||||||
* @author Arthur van Hoff
|
|
||||||
* @see java.io.BufferedOutputStream
|
|
||||||
* @see java.io.ByteArrayOutputStream
|
|
||||||
* @see java.io.DataOutputStream
|
|
||||||
* @see java.io.FilterOutputStream
|
|
||||||
* @see java.io.InputStream
|
|
||||||
* @see java.io.OutputStream#write(int)
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public abstract class SafeOutputStream extends OutputStream implements Closeable, Flushable {
|
|
||||||
/**
|
|
||||||
* Constructor for subclasses to call.
|
|
||||||
*/
|
|
||||||
public SafeOutputStream() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new {@code OutputStream} which discards all bytes. The
|
|
||||||
* returned stream is initially open. The stream is closed by calling
|
|
||||||
* the {@code close()} method. Subsequent calls to {@code close()} have
|
|
||||||
* no effect.
|
|
||||||
*
|
|
||||||
* <p> While the stream is open, the {@code write(int)}, {@code
|
|
||||||
* write(byte[])}, and {@code write(byte[], int, int)} methods do nothing.
|
|
||||||
* After the stream has been closed, these methods all throw {@code
|
|
||||||
* IOException}.
|
|
||||||
*
|
|
||||||
* <p> The {@code flush()} method does nothing.
|
|
||||||
*
|
|
||||||
* @return an {@code OutputStream} which discards all bytes
|
|
||||||
*
|
|
||||||
* @since 11
|
|
||||||
*/
|
|
||||||
public static java.io.OutputStream nullOutputStream() {
|
|
||||||
return new java.io.OutputStream() {
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
private void ensureOpen() {
|
|
||||||
if (closed) {
|
|
||||||
throw new IllegalStateException("Stream closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int b) {
|
|
||||||
ensureOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte @NotNull [] b, int off, int len) {
|
|
||||||
Objects.checkFromIndexSize(off, len, b.length);
|
|
||||||
ensureOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the specified byte to this output stream. The general
|
|
||||||
* contract for {@code write} is that one byte is written
|
|
||||||
* to the output stream. The byte to be written is the eight
|
|
||||||
* low-order bits of the argument {@code b}. The 24
|
|
||||||
* high-order bits of {@code b} are ignored.
|
|
||||||
* <p>
|
|
||||||
* Subclasses of {@code OutputStream} must provide an
|
|
||||||
* implementation for this method.
|
|
||||||
*
|
|
||||||
* @param b the {@code byte}.
|
|
||||||
*/
|
|
||||||
public abstract void write(int b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code b.length} bytes from the specified byte array
|
|
||||||
* to this output stream. The general contract for {@code write(b)}
|
|
||||||
* is that it should have exactly the same effect as the call
|
|
||||||
* {@code write(b, 0, b.length)}.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
* @see java.io.OutputStream#write(byte[], int, int)
|
|
||||||
*/
|
|
||||||
public void write(byte @NotNull [] b) {
|
|
||||||
write(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes {@code len} bytes from the specified byte array
|
|
||||||
* starting at offset {@code off} to this output stream.
|
|
||||||
* The general contract for {@code write(b, off, len)} is that
|
|
||||||
* some of the bytes in the array {@code b} are written to the
|
|
||||||
* output stream in order; element {@code b[off]} is the first
|
|
||||||
* byte written and {@code b[off+len-1]} is the last byte written
|
|
||||||
* by this operation.
|
|
||||||
* <p>
|
|
||||||
* The {@code write} method of {@code OutputStream} calls
|
|
||||||
* the write method of one argument on each of the bytes to be
|
|
||||||
* written out. Subclasses are encouraged to override this method and
|
|
||||||
* provide a more efficient implementation.
|
|
||||||
* <p>
|
|
||||||
* If {@code b} is {@code null}, a
|
|
||||||
* {@code NullPointerException} is thrown.
|
|
||||||
* <p>
|
|
||||||
* If {@code off} is negative, or {@code len} is negative, or
|
|
||||||
* {@code off+len} is greater than the length of the array
|
|
||||||
* {@code b}, then an {@code IndexOutOfBoundsException} is thrown.
|
|
||||||
*
|
|
||||||
* @param b the data.
|
|
||||||
* @param off the start offset in the data.
|
|
||||||
* @param len the number of bytes to write.
|
|
||||||
*/
|
|
||||||
public void write(byte[] b, int off, int len) {
|
|
||||||
Objects.checkFromIndexSize(off, len, b.length);
|
|
||||||
// len == 0 condition implicitly handled by loop bounds
|
|
||||||
for (int i = 0 ; i < len ; i++) {
|
|
||||||
write(b[off + i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flushes this output stream and forces any buffered output bytes
|
|
||||||
* to be written out. The general contract of {@code flush} is
|
|
||||||
* that calling it is an indication that, if any bytes previously
|
|
||||||
* written have been buffered by the implementation of the output
|
|
||||||
* stream, such bytes should immediately be written to their
|
|
||||||
* intended destination.
|
|
||||||
* <p>
|
|
||||||
* If the intended destination of this stream is an abstraction provided by
|
|
||||||
* the underlying operating system, for example a file, then flushing the
|
|
||||||
* stream guarantees only that bytes previously written to the stream are
|
|
||||||
* passed to the operating system for writing; it does not guarantee that
|
|
||||||
* they are actually written to a physical device such as a disk drive.
|
|
||||||
* <p>
|
|
||||||
* The {@code flush} method of {@code OutputStream} does nothing.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void flush() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this output stream and releases any system resources
|
|
||||||
* associated with this stream. The general contract of {@code close}
|
|
||||||
* is that it closes the output stream. A closed stream cannot perform
|
|
||||||
* output operations and cannot be reopened.
|
|
||||||
* <p>
|
|
||||||
* The {@code close} method of {@code OutputStream} does nothing.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,332 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@code PushbackInputStream} adds
|
|
||||||
* functionality to another input stream, namely
|
|
||||||
* the ability to "push back" or "unread" bytes,
|
|
||||||
* by storing pushed-back bytes in an internal buffer.
|
|
||||||
* This is useful in situations where
|
|
||||||
* it is convenient for a fragment of code
|
|
||||||
* to read an indefinite number of data bytes
|
|
||||||
* that are delimited by a particular byte
|
|
||||||
* value; after reading the terminating byte,
|
|
||||||
* the code fragment can "unread" it, so that
|
|
||||||
* the next read operation on the input stream
|
|
||||||
* will reread the byte that was pushed back.
|
|
||||||
* For example, bytes representing the characters
|
|
||||||
* constituting an identifier might be terminated
|
|
||||||
* by a byte representing an operator character;
|
|
||||||
* a method whose job is to read just an identifier
|
|
||||||
* can read until it sees the operator and
|
|
||||||
* then push the operator back to be re-read.
|
|
||||||
*
|
|
||||||
* @author David Connelly
|
|
||||||
* @author Jonathan Payne
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
public class SafePushbackInputStream extends SafeFilterInputStream {
|
|
||||||
/**
|
|
||||||
* The pushback buffer.
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
protected byte[] buf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The position within the pushback buffer from which the next byte will
|
|
||||||
* be read. When the buffer is empty, {@code pos} is equal to
|
|
||||||
* {@code buf.length}; when the buffer is full, {@code pos} is
|
|
||||||
* equal to zero.
|
|
||||||
*
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
protected int pos;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to make sure that this stream has not been closed
|
|
||||||
*/
|
|
||||||
private void ensureOpen() {
|
|
||||||
if (in == null)
|
|
||||||
throw new IllegalStateException("Stream closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@code PushbackInputStream}
|
|
||||||
* with a pushback buffer of the specified {@code size},
|
|
||||||
* and saves its argument, the input stream
|
|
||||||
* {@code in}, for later use. Initially,
|
|
||||||
* the pushback buffer is empty.
|
|
||||||
*
|
|
||||||
* @param in the input stream from which bytes will be read.
|
|
||||||
* @param size the size of the pushback buffer.
|
|
||||||
* @throws IllegalArgumentException if {@code size <= 0}
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
public SafePushbackInputStream(SafeInputStream in, int size) {
|
|
||||||
super(in);
|
|
||||||
if (size <= 0) {
|
|
||||||
throw new IllegalArgumentException("size <= 0");
|
|
||||||
}
|
|
||||||
this.buf = new byte[size];
|
|
||||||
this.pos = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@code PushbackInputStream}
|
|
||||||
* with a 1-byte pushback buffer, and saves its argument, the input stream
|
|
||||||
* {@code in}, for later use. Initially,
|
|
||||||
* the pushback buffer is empty.
|
|
||||||
*
|
|
||||||
* @param in the input stream from which bytes will be read.
|
|
||||||
*/
|
|
||||||
public SafePushbackInputStream(SafeInputStream in) {
|
|
||||||
this(in, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the next byte of data from this input stream. The value
|
|
||||||
* byte is returned as an {@code int} in the range
|
|
||||||
* {@code 0} to {@code 255}. If no byte is available
|
|
||||||
* because the end of the stream has been reached, the value
|
|
||||||
* {@code -1} is returned. This method blocks until input data
|
|
||||||
* is available, the end of the stream is detected, or an exception
|
|
||||||
* is thrown.
|
|
||||||
*
|
|
||||||
* <p> This method returns the most recently pushed-back byte, if there is
|
|
||||||
* one, and otherwise calls the {@code read} method of its underlying
|
|
||||||
* input stream and returns whatever value that method returns.
|
|
||||||
*
|
|
||||||
* @return the next byte of data, or {@code -1} if the end of the
|
|
||||||
* stream has been reached.
|
|
||||||
* or an I/O error occurs.
|
|
||||||
* @see java.io.InputStream#read()
|
|
||||||
*/
|
|
||||||
public int read() {
|
|
||||||
ensureOpen();
|
|
||||||
if (pos < buf.length) {
|
|
||||||
return buf[pos++] & 0xff;
|
|
||||||
}
|
|
||||||
return super.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to {@code len} bytes of data from this input stream into
|
|
||||||
* an array of bytes. This method first reads any pushed-back bytes; after
|
|
||||||
* that, if fewer than {@code len} bytes have been read then it
|
|
||||||
* reads from the underlying input stream. If {@code len} is not zero, the method
|
|
||||||
* blocks until at least 1 byte of input is available; otherwise, no
|
|
||||||
* bytes are read and {@code 0} is returned.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @param off the start offset in the destination array {@code b}
|
|
||||||
* @param len the maximum number of bytes read.
|
|
||||||
* @return the total number of bytes read into the buffer, or
|
|
||||||
* {@code -1} if there is no more data because the end of
|
|
||||||
* the stream has been reached.
|
|
||||||
* @throws NullPointerException If {@code b} is {@code null}.
|
|
||||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
|
||||||
* {@code len} is negative, or {@code len} is greater than
|
|
||||||
* {@code b.length - off}
|
|
||||||
* or an I/O error occurs.
|
|
||||||
* @see java.io.InputStream#read(byte[], int, int)
|
|
||||||
*/
|
|
||||||
public int read(byte[] b, int off, int len) {
|
|
||||||
ensureOpen();
|
|
||||||
if (b == null) {
|
|
||||||
throw new NullPointerException();
|
|
||||||
} else if (off < 0 || len < 0 || len > b.length - off) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
} else if (len == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int avail = buf.length - pos;
|
|
||||||
if (avail > 0) {
|
|
||||||
if (len < avail) {
|
|
||||||
avail = len;
|
|
||||||
}
|
|
||||||
System.arraycopy(buf, pos, b, off, avail);
|
|
||||||
pos += avail;
|
|
||||||
off += avail;
|
|
||||||
len -= avail;
|
|
||||||
}
|
|
||||||
if (len > 0) {
|
|
||||||
len = super.read(b, off, len);
|
|
||||||
if (len == -1) {
|
|
||||||
return avail == 0 ? -1 : avail;
|
|
||||||
}
|
|
||||||
return avail + len;
|
|
||||||
}
|
|
||||||
return avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes back a byte by copying it to the front of the pushback buffer.
|
|
||||||
* After this method returns, the next byte to be read will have the value
|
|
||||||
* {@code (byte)b}.
|
|
||||||
*
|
|
||||||
* @param b the {@code int} value whose low-order
|
|
||||||
* byte is to be pushed back.
|
|
||||||
*/
|
|
||||||
public void unread(int b) {
|
|
||||||
ensureOpen();
|
|
||||||
if (pos == 0) {
|
|
||||||
throw new IllegalStateException("Push back buffer is full");
|
|
||||||
}
|
|
||||||
buf[--pos] = (byte)b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes back a portion of an array of bytes by copying it to the front
|
|
||||||
* of the pushback buffer. After this method returns, the next byte to be
|
|
||||||
* read will have the value {@code b[off]}, the byte after that will
|
|
||||||
* have the value {@code b[off+1]}, and so forth.
|
|
||||||
*
|
|
||||||
* @param b the byte array to push back.
|
|
||||||
* @param off the start offset of the data.
|
|
||||||
* @param len the number of bytes to push back.
|
|
||||||
* @throws NullPointerException If {@code b} is {@code null}.
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
public void unread(byte[] b, int off, int len) {
|
|
||||||
ensureOpen();
|
|
||||||
if (len > pos) {
|
|
||||||
throw new IllegalStateException("Push back buffer is full");
|
|
||||||
}
|
|
||||||
pos -= len;
|
|
||||||
System.arraycopy(b, off, buf, pos, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes back an array of bytes by copying it to the front of the
|
|
||||||
* pushback buffer. After this method returns, the next byte to be read
|
|
||||||
* will have the value {@code b[0]}, the byte after that will have the
|
|
||||||
* value {@code b[1]}, and so forth.
|
|
||||||
*
|
|
||||||
* @param b the byte array to push back
|
|
||||||
* @throws NullPointerException If {@code b} is {@code null}.
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
public void unread(byte[] b) {
|
|
||||||
unread(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an estimate of the number of bytes that can be read (or
|
|
||||||
* skipped over) from this input stream without blocking by the next
|
|
||||||
* invocation of a method for this input stream. The next invocation might be
|
|
||||||
* the same thread or another thread. A single read or skip of this
|
|
||||||
* many bytes will not block, but may read or skip fewer bytes.
|
|
||||||
*
|
|
||||||
* <p> The method returns the sum of the number of bytes that have been
|
|
||||||
* pushed back and the value returned by {@link
|
|
||||||
* SafeFilterInputStream#available available}.
|
|
||||||
*
|
|
||||||
* @return the number of bytes that can be read (or skipped over) from
|
|
||||||
* the input stream without blocking.
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see java.io.InputStream#available()
|
|
||||||
*/
|
|
||||||
public int available() {
|
|
||||||
ensureOpen();
|
|
||||||
int n = buf.length - pos;
|
|
||||||
int avail = super.available();
|
|
||||||
return n > (Integer.MAX_VALUE - avail)
|
|
||||||
? Integer.MAX_VALUE
|
|
||||||
: n + avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips over and discards {@code n} bytes of data from this
|
|
||||||
* input stream. The {@code skip} method may, for a variety of
|
|
||||||
* reasons, end up skipping over some smaller number of bytes,
|
|
||||||
* possibly zero. If {@code n} is negative, no bytes are skipped.
|
|
||||||
*
|
|
||||||
* <p> The {@code skip} method of {@code PushbackInputStream}
|
|
||||||
* first skips over the bytes in the pushback buffer, if any. It then
|
|
||||||
* calls the {@code skip} method of the underlying input stream if
|
|
||||||
* more bytes need to be skipped. The actual number of bytes skipped
|
|
||||||
* is returned.
|
|
||||||
*
|
|
||||||
* @param n {@inheritDoc}
|
|
||||||
* @return {@inheritDoc}
|
|
||||||
* @see SafeFilterInputStream#in
|
|
||||||
* @see java.io.InputStream#skip(long n)
|
|
||||||
* @since 1.2
|
|
||||||
*/
|
|
||||||
public long skip(long n) {
|
|
||||||
ensureOpen();
|
|
||||||
if (n <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long pskip = buf.length - pos;
|
|
||||||
if (pskip > 0) {
|
|
||||||
if (n < pskip) {
|
|
||||||
pskip = n;
|
|
||||||
}
|
|
||||||
pos += pskip;
|
|
||||||
n -= pskip;
|
|
||||||
}
|
|
||||||
if (n > 0) {
|
|
||||||
pskip += super.skip(n);
|
|
||||||
}
|
|
||||||
return pskip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests if this input stream supports the {@code mark} and
|
|
||||||
* {@code reset} methods, which it does not.
|
|
||||||
*
|
|
||||||
* @return {@code false}, since this class does not support the
|
|
||||||
* {@code mark} and {@code reset} methods.
|
|
||||||
* @see java.io.InputStream#mark(int)
|
|
||||||
* @see java.io.InputStream#reset()
|
|
||||||
*/
|
|
||||||
public boolean markSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the current position in this input stream.
|
|
||||||
*
|
|
||||||
* <p> The {@code mark} method of {@code PushbackInputStream}
|
|
||||||
* does nothing.
|
|
||||||
*
|
|
||||||
* @param readlimit the maximum limit of bytes that can be read before
|
|
||||||
* the mark position becomes invalid.
|
|
||||||
* @see java.io.InputStream#reset()
|
|
||||||
*/
|
|
||||||
public synchronized void mark(int readlimit) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repositions this stream to the position at the time the
|
|
||||||
* {@code mark} method was last called on this input stream.
|
|
||||||
*
|
|
||||||
* <p> The method {@code reset} for class
|
|
||||||
* {@code PushbackInputStream} does nothing except throw an
|
|
||||||
* {@code IOException}.
|
|
||||||
*
|
|
||||||
* @see java.io.InputStream#mark(int)
|
|
||||||
* @see java.io.IOException
|
|
||||||
*/
|
|
||||||
public synchronized void reset() {
|
|
||||||
throw new UnsupportedOperationException("mark/reset not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this input stream and releases any system resources
|
|
||||||
* associated with the stream.
|
|
||||||
* Once the stream has been closed, further read(), unread(),
|
|
||||||
* available(), reset(), or skip() invocations will throw an IOException.
|
|
||||||
* Closing a previously closed stream has no effect.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public synchronized void close() {
|
|
||||||
if (in == null)
|
|
||||||
return;
|
|
||||||
in.close();
|
|
||||||
in = null;
|
|
||||||
buf = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package org.warp.commonutils.stream;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/** A basic interface specifying positioning methods for a byte stream.
|
|
||||||
*
|
|
||||||
* @author Sebastiano Vigna
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
|
|
||||||
public interface SafeRepositionableStream {
|
|
||||||
|
|
||||||
/** Sets the current stream position.
|
|
||||||
*
|
|
||||||
* @param newPosition the new stream position.
|
|
||||||
*/
|
|
||||||
void position(long newPosition);
|
|
||||||
|
|
||||||
/** Returns the current stream position.
|
|
||||||
*
|
|
||||||
* @return the current stream position.
|
|
||||||
*/
|
|
||||||
long position();
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
public enum AddStrategy {
|
|
||||||
OVERWRITE_POSITION,
|
|
||||||
KEEP_POSITION;
|
|
||||||
|
|
||||||
public static AddStrategy getDefault() {
|
|
||||||
return KEEP_POSITION;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class ArrayStack<T> extends FastUtilStackWrapper<T> {
|
|
||||||
|
|
||||||
public ArrayStack() {
|
|
||||||
super(new ObjectArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayStack(Collection<T> stack) {
|
|
||||||
super(new ObjectArrayList<>(stack));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class Bytes {
|
|
||||||
public final byte[] data;
|
|
||||||
|
|
||||||
public Bytes(byte @NotNull[] data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<? extends Bytes,? extends Bytes> ofMap(Map<byte[], byte[]> oldMap) {
|
|
||||||
var newMap = new HashMap<Bytes, Bytes>(oldMap.size());
|
|
||||||
oldMap.forEach((key, value) -> newMap.put(new Bytes(key), new Bytes(value)));
|
|
||||||
return newMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<? extends Bytes> ofList(List<byte[]> oldList) {
|
|
||||||
var newList = new ArrayList<Bytes>(oldList.size());
|
|
||||||
oldList.forEach((item) -> newList.add(new Bytes(item)));
|
|
||||||
return newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set<? extends Bytes> ofSet(Set<byte[]> oldSet) {
|
|
||||||
var newSet = new ObjectOpenHashSet<Bytes>(oldSet.size());
|
|
||||||
oldSet.forEach((item) -> newSet.add(new Bytes(item)));
|
|
||||||
return newSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[][] toByteArray(Collection<Bytes> value) {
|
|
||||||
Bytes[] valueBytesArray = value.toArray(Bytes[]::new);
|
|
||||||
byte[][] convertedResult = new byte[valueBytesArray.length][];
|
|
||||||
for (int i = 0; i < valueBytesArray.length; i++) {
|
|
||||||
convertedResult[i] = valueBytesArray[i].data;
|
|
||||||
}
|
|
||||||
return convertedResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Bytes that = (Bytes) o;
|
|
||||||
return Arrays.equals(data, that.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Arrays.hashCode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Arrays.toString(data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class EqualsWrapper<T> {
|
|
||||||
|
|
||||||
private final T value;
|
|
||||||
private final Object equal;
|
|
||||||
|
|
||||||
public EqualsWrapper(T value, Object equal) {
|
|
||||||
this.value = value;
|
|
||||||
this.equal = equal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EqualsWrapper<?> that = (EqualsWrapper<?>) o;
|
|
||||||
return Objects.equals(equal, that.equal);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(equal);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class FastUtilStackWrapper<T> implements Stack<T>, Collection<T> {
|
|
||||||
|
|
||||||
private final it.unimi.dsi.fastutil.Stack<T> stack;
|
|
||||||
private final Collection<T> collection;
|
|
||||||
|
|
||||||
public <U extends it.unimi.dsi.fastutil.Stack<T> & Collection<T>> FastUtilStackWrapper(U stack) {
|
|
||||||
this.stack = stack;
|
|
||||||
this.collection = stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void push(T o) {
|
|
||||||
this.stack.push(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T pop() {
|
|
||||||
return this.stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T peek(int n) {
|
|
||||||
return this.stack.peek(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return collection.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.stack.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object o) {
|
|
||||||
return collection.contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return collection.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Object @NotNull [] toArray() {
|
|
||||||
return collection.toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public <T1> T1 @NotNull [] toArray(@NotNull T1 @NotNull [] a) {
|
|
||||||
//noinspection SuspiciousToArrayCall
|
|
||||||
return collection.toArray(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(T t) {
|
|
||||||
return collection.add(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(Object o) {
|
|
||||||
return collection.remove(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAll(@NotNull Collection<?> c) {
|
|
||||||
return collection.containsAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(@NotNull Collection<? extends T> c) {
|
|
||||||
return collection.addAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeAll(@NotNull Collection<?> c) {
|
|
||||||
return collection.removeAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean retainAll(@NotNull Collection<?> c) {
|
|
||||||
return collection.retainAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
collection.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FastUtilStackWrapper<?> that = (FastUtilStackWrapper<?>) o;
|
|
||||||
|
|
||||||
return Objects.equals(stack, that.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return stack != null ? stack.hashCode() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", FastUtilStackWrapper.class.getSimpleName() + "[", "]")
|
|
||||||
.add("stack=" + stack)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Spliterator;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.IntFunction;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class ImmutableLinkedSet<E> implements Set<E> {
|
|
||||||
|
|
||||||
private final LinkedHashSet<E> set;
|
|
||||||
|
|
||||||
public ImmutableLinkedSet(LinkedHashSet<E> set) {
|
|
||||||
this.set = set;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return set.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return set.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object o) {
|
|
||||||
return set.contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<E> iterator() {
|
|
||||||
var it = set.iterator();
|
|
||||||
return new Iterator<>() {
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public E next() {
|
|
||||||
return it.next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEach(Consumer<? super E> action) {
|
|
||||||
set.forEach(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Object @NotNull [] toArray() {
|
|
||||||
return set.toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] a) {
|
|
||||||
//noinspection SuspiciousToArrayCall
|
|
||||||
return set.toArray(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T[] toArray(IntFunction<T[]> generator) {
|
|
||||||
//noinspection SuspiciousToArrayCall
|
|
||||||
return set.toArray(generator);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(E e) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(Object o) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAll(@NotNull Collection<?> c) {
|
|
||||||
return set.containsAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(@NotNull Collection<? extends E> c) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean retainAll(@NotNull Collection<?> c) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeAll(@NotNull Collection<?> c) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeIf(Predicate<? super E> filter) {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
throw new UnsupportedOperationException("Read only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spliterator<E> spliterator() {
|
|
||||||
var spl = set.spliterator();
|
|
||||||
return new Spliterator<>() {
|
|
||||||
@Override
|
|
||||||
public boolean tryAdvance(Consumer<? super E> action) {
|
|
||||||
return spl.tryAdvance(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spliterator<E> trySplit() {
|
|
||||||
return spl.trySplit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long estimateSize() {
|
|
||||||
return spl.estimateSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int characteristics() {
|
|
||||||
return spl.characteristics();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<E> stream() {
|
|
||||||
return set.stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<E> parallelStream() {
|
|
||||||
return set.parallelStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LinkedHashSet<E> copyMutable() {
|
|
||||||
return new LinkedHashSet<>(set);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One to many relationship
|
|
||||||
*
|
|
||||||
* o ------- o
|
|
||||||
* +--- o
|
|
||||||
* o ---+--- o
|
|
||||||
* +-|- o
|
|
||||||
* o -----|- o
|
|
||||||
* o ------- o
|
|
||||||
* o ---+--- o
|
|
||||||
* +--- o
|
|
||||||
*
|
|
||||||
* @param <T> Source type
|
|
||||||
* @param <U> Destination type
|
|
||||||
*/
|
|
||||||
public interface MultiAssociation<T, U> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Link source to dest
|
|
||||||
* @param src Source
|
|
||||||
* @param dest Destination
|
|
||||||
* @return true if linked, false if it was already linked with that destination
|
|
||||||
*/
|
|
||||||
boolean link(T src, U dest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlink only if src is linked with dest
|
|
||||||
* @param src Source
|
|
||||||
* @param dest Destination
|
|
||||||
* @return true if unlinked, false if not present
|
|
||||||
*/
|
|
||||||
boolean unlink(T src, U dest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlink
|
|
||||||
* @param src Source
|
|
||||||
* @return previous linked destinations
|
|
||||||
*/
|
|
||||||
Set<U> unlink(T src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlink
|
|
||||||
* @param dest Destination
|
|
||||||
* @return previous linked source
|
|
||||||
*/
|
|
||||||
Set<T> unlinkFromSource(U dest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if link exists
|
|
||||||
* @param src Source
|
|
||||||
* @return true if source is linked with at least 1 destination
|
|
||||||
*/
|
|
||||||
default boolean hasAnyLink(T src) {
|
|
||||||
return !getLinks(src).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if link exists
|
|
||||||
* @param dest Destination
|
|
||||||
* @return true if destination is linked with at least 1 destination
|
|
||||||
*/
|
|
||||||
default boolean hasAnyLinkSource(U dest) {
|
|
||||||
return !getLinkSources(dest).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if link exists
|
|
||||||
* @param src Source
|
|
||||||
* @param dest Destination
|
|
||||||
* @return true if source and destination are linked together
|
|
||||||
*/
|
|
||||||
boolean hasLink(T src, U dest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a link destination
|
|
||||||
* @param src Source
|
|
||||||
* @return Existing linked destinations
|
|
||||||
*/
|
|
||||||
Set<U> getLinks(T src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a link source
|
|
||||||
* @param dest Source
|
|
||||||
* @return Existing linked sources
|
|
||||||
*/
|
|
||||||
Set<T> getLinkSources(U dest);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all links
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the count of existing links
|
|
||||||
* @return size
|
|
||||||
*/
|
|
||||||
int size();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all the sources
|
|
||||||
* @return Set of sources
|
|
||||||
*/
|
|
||||||
ObjectSet<T> getSources();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all the destinations
|
|
||||||
* @return Set of destinations
|
|
||||||
*/
|
|
||||||
ObjectSet<U> getDestinations();
|
|
||||||
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A default {@link ThreadFactory} implementation that accepts the name prefix
|
|
||||||
* of the created threads as a constructor argument. Otherwise, this factory
|
|
||||||
* yields the same semantics as the thread factory returned by
|
|
||||||
* {@link Executors#defaultThreadFactory()}.
|
|
||||||
*/
|
|
||||||
public class ShortNamedThreadFactory implements ThreadFactory {
|
|
||||||
|
|
||||||
private static int POOL_NUMBERS_COUNT = 50;
|
|
||||||
private static final AtomicInteger[] threadPoolNumber = new AtomicInteger[POOL_NUMBERS_COUNT];
|
|
||||||
static {
|
|
||||||
for (int i = 0; i < threadPoolNumber.length; i++) {
|
|
||||||
threadPoolNumber[i] = new AtomicInteger(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private ThreadGroup group;
|
|
||||||
private boolean daemon;
|
|
||||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
|
||||||
private static final String NAME_PATTERN = "%s-%d";
|
|
||||||
private final String threadNamePrefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link ShortNamedThreadFactory} instance
|
|
||||||
*
|
|
||||||
* @param threadNamePrefix the name prefix assigned to each thread created.
|
|
||||||
*/
|
|
||||||
public ShortNamedThreadFactory(String threadNamePrefix) {
|
|
||||||
group = Thread.currentThread().getThreadGroup();
|
|
||||||
this.threadNamePrefix = String.format(Locale.ROOT, NAME_PATTERN,
|
|
||||||
checkPrefix(threadNamePrefix), threadPoolNumber[(threadNamePrefix.hashCode() % POOL_NUMBERS_COUNT / 2) + POOL_NUMBERS_COUNT / 2].getAndIncrement());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShortNamedThreadFactory withGroup(ThreadGroup threadGroup) {
|
|
||||||
this.group = threadGroup;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShortNamedThreadFactory setDaemon(boolean daemon) {
|
|
||||||
this.daemon = daemon;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String checkPrefix(String prefix) {
|
|
||||||
return prefix == null || prefix.length() == 0 ? "Unnamed" : prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link Thread}
|
|
||||||
*
|
|
||||||
* @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Thread newThread(@NotNull Runnable r) {
|
|
||||||
final Thread t = new Thread(group, r, String.format(Locale.ROOT, "%s-%d",
|
|
||||||
this.threadNamePrefix, threadNumber.getAndIncrement()), 0);
|
|
||||||
t.setDaemon(daemon);
|
|
||||||
t.setPriority(Thread.NORM_PRIORITY);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import org.warp.commonutils.error.IndexOutOfBoundsException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stack
|
|
||||||
*
|
|
||||||
* <p>A stack must provide the classical {@link #push(Object)} and
|
|
||||||
* {@link #pop()} operations, but may be also <em>peekable</em> to some extent: it may provide just the {@link #top()}
|
|
||||||
* function, or even a more powerful {@link #peek(int)} method that provides access to all elements on the stack
|
|
||||||
* (indexed from the top, which has index 0).
|
|
||||||
*/
|
|
||||||
|
|
||||||
public interface Stack<K> extends it.unimi.dsi.fastutil.Stack<K> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop multiple times
|
|
||||||
* @param count the number of times to pop
|
|
||||||
* @return list of popped elements
|
|
||||||
*/
|
|
||||||
default List<K> pop(int count) {
|
|
||||||
if (count < 0) {
|
|
||||||
throw new IndexOutOfBoundsException(count);
|
|
||||||
}
|
|
||||||
var items = new ArrayList<K>(count);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
items.add(this.pop());
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> Stack<T> create() {
|
|
||||||
return new ArrayStack<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T, U extends Stack<T> & Collection<T>> Stack<T> wrap(U stack) {
|
|
||||||
return new FastUtilStackWrapper<>(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.warp.commonutils.type;
|
|
||||||
|
|
||||||
public class VariableWrapper<T> {
|
|
||||||
|
|
||||||
public volatile T var;
|
|
||||||
|
|
||||||
public VariableWrapper(T value) {
|
|
||||||
this.var = value;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user