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>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-utils</artifactId>
|
||||
<groupId>org.warp</groupId>
|
||||
<version>1.1.8</version>
|
||||
<groupId>it.tdlight</groupId>
|
||||
<artifactId>common-util</artifactId>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.0-SNAPSHOT</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@ -35,6 +36,11 @@
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jctools</groupId>
|
||||
<artifactId>jctools-core</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
@ -47,65 +53,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</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>
|
||||
<build>
|
||||
<plugins>
|
||||
@ -115,7 +62,7 @@
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<release>11</release>
|
||||
<release>8</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@ -136,6 +83,31 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</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>
|
||||
</build>
|
||||
</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