Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec6506dae0 | ||
|
|
b7361cb475 | ||
|
|
f340758d89 | ||
|
|
099ca8bb97 | ||
|
|
f88dc25d1a | ||
|
|
11691fa018 | ||
|
|
b4da69103e | ||
|
|
6d72439819 | ||
|
|
e02470e762 | ||
|
|
651327f439 | ||
|
|
a309ff8645 | ||
|
|
4068c8b735 | ||
|
|
74a1a7778e | ||
|
|
1488bc7276 | ||
|
|
16e976bfff | ||
|
|
4c3483f207 | ||
|
|
2b9816ce80 | ||
|
|
a8f3cc126d | ||
|
|
21d9c21a8c | ||
|
|
1892bec80f | ||
|
|
39496fd63f | ||
|
|
7f904c6e7f | ||
|
|
6a3ac364ab | ||
|
|
5d84b5c254 | ||
|
|
debc0a0410 | ||
|
|
4722f50a7c | ||
|
|
d671bf7446 | ||
|
|
bec6f94b5f | ||
|
|
4f35b77548 | ||
|
|
cbb63ff15f | ||
|
|
772546291b | ||
|
|
032aa9e5e0 | ||
|
|
6f5d152cbd | ||
|
|
da66be3d57 | ||
|
|
15940b4357 | ||
|
|
1e9fdcae07 | ||
|
|
0a34b72a26 | ||
|
|
24a19e3e2f | ||
|
|
9362ce762c | ||
|
|
bfd2b19190 | ||
|
|
c78381a20a | ||
|
|
06e16367f1 | ||
|
|
72565964f2 | ||
|
|
03edbba8c4 | ||
|
|
908372c235 | ||
|
|
4e3872519a | ||
|
|
3f68527616 | ||
|
|
c0487ce0c3 | ||
|
|
4dc8540461 | ||
|
|
9d40b98a4c | ||
|
|
5c01dad173 | ||
|
|
a5328702af | ||
|
|
fc17fe853e | ||
|
|
80fbed262d | ||
|
|
a43e45f223 | ||
|
|
e21069fd11 | ||
|
|
4d8552ac66 | ||
|
|
2bcf3c6421 | ||
|
|
9331dccb8b | ||
|
|
424e9ce897 | ||
|
|
5679c26e99 | ||
|
|
d732892673 | ||
|
|
df32e610b1 | ||
|
|
15a6e5b222 | ||
|
|
89e0bb7cdc | ||
|
|
69ab704151 | ||
|
|
e11ce37438 | ||
|
|
db9c93bdf5 | ||
|
|
ecb061f65c | ||
|
|
21fe741eb8 | ||
|
|
5167e4c9b5 | ||
|
|
44d6ee0df6 | ||
|
|
ae3d07ebdd | ||
|
|
a90fedbc4c | ||
|
|
97c6694fd2 | ||
|
|
b863035f09 | ||
|
|
4e7279c766 | ||
|
|
16f8793308 | ||
|
|
6f8388a0b5 | ||
|
|
c02666d357 | ||
|
|
1df4d47ca6 | ||
|
|
0f9b9380cf | ||
|
|
a6b38be5a8 | ||
|
|
4775da61fc | ||
|
|
2e306e87ff | ||
|
|
8a71c42398 | ||
|
|
0c5e8ec482 | ||
|
|
f082e7209c | ||
|
|
0e9412af7f | ||
|
|
38e92e0fc3 | ||
|
|
3e146185df | ||
|
|
16c44bc7cd | ||
|
|
5ac5351dbe | ||
|
|
dbcfa995fb | ||
|
|
16bc17921c | ||
|
|
eb3b9f1e4a | ||
|
|
f1f274f5af | ||
|
|
1b0d715004 | ||
|
|
98cceb8184 | ||
|
|
7012192995 | ||
|
|
8d9e91693a | ||
|
|
604f967634 | ||
|
|
b7f5bf70bf | ||
|
|
dda92b2656 | ||
|
|
98f55f3554 | ||
|
|
29f2ec28d6 | ||
|
|
cf6ddbfebb | ||
|
|
608e788673 | ||
|
|
1f85b5941a | ||
|
|
beff277f6c | ||
|
|
4d0e36932c | ||
|
|
75ee4dc363 | ||
|
|
490eee7146 | ||
|
|
954f91410b | ||
|
|
7051207876 | ||
|
|
39475b6343 | ||
|
|
57044959b1 | ||
|
|
721c5f0cdc | ||
|
|
3722d5d28a | ||
|
|
39eca4ceea | ||
|
|
6691b83558 | ||
|
|
341fb4ddad | ||
|
|
eec740a735 | ||
|
|
eb14750c3f | ||
|
|
924d69bdc9 | ||
|
|
4340d3d3d3 | ||
|
|
ee07614fe7 | ||
|
|
713b2d37d6 | ||
|
|
1c1d890f65 | ||
|
|
950699102d | ||
|
|
6b79cdc3d0 | ||
|
|
2569575340 | ||
|
|
a542cb3cb4 |
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+2
-1
@@ -4,13 +4,14 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/file_picker" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
|
||||
Generated
+8
-3
@@ -10,12 +10,13 @@
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="15">
|
||||
<list size="17">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||
@@ -31,12 +32,14 @@
|
||||
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
|
||||
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
|
||||
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
|
||||
<item index="15" class="java.lang.String" itemvalue="org.jspecify.annotations.Nullable" />
|
||||
<item index="16" class="java.lang.String" itemvalue="jakarta.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="15">
|
||||
<list size="17">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
@@ -52,11 +55,13 @@
|
||||
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
|
||||
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
|
||||
<item index="14" class="java.lang.String" itemvalue="lombok.NonNull" />
|
||||
<item index="15" class="java.lang.String" itemvalue="org.jspecify.annotations.NonNull" />
|
||||
<item index="16" class="java.lang.String" itemvalue="jakarta.annotation.Nonnull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
+1
-1
@@ -6,7 +6,7 @@ When contributing to this repository, it's recommended to first discuss the chan
|
||||
|
||||
Please see project's webpage for some additional information:
|
||||
|
||||
https://bined.exbin.org/?participate
|
||||
https://bined.exbin.org/?p=participate
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
BinEd - Binary/Hex Editor
|
||||
=========================
|
||||
BinEd - Hex Editor
|
||||
==================
|
||||
|
||||
Editor for binary data (hex viewer/editor) written in Java.
|
||||
|
||||
Homepage: https://bined.exbin.org/android
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/packages/org.exbin.bined.editor.android/)[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="80">](https://play.google.com/store/apps/details?id=org.exbin.bined.editor.android)[<img src="https://github.com/user-attachments/assets/713d71c5-3dec-4ec4-a3f2-8d28d025a9c6" alt="Get it on Obtainium" height="80">](http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22org.exbin.bined.editor.android%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fexbin%2Fbined-android%22%2C%22author%22%3A%22ExBin%20Project%22%2C%22name%22%3A%22BinEd%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%7D%22%7D)[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyDroid" height="80">](https://android.izzysoft.de/repo/apk/org.exbin.bined.editor.android)
|
||||
|
||||
Or download the latest APK from the [Releases Section](https://github.com/exbin/bined-android/releases/latest).
|
||||
|
||||
Screenshot
|
||||
----------
|
||||
|
||||
@@ -13,22 +17,32 @@ Screenshot
|
||||
Features
|
||||
--------
|
||||
|
||||
UNDER DEVELOPMENT
|
||||
|
||||
* Visualize data as numerical (hexadecimal) codes and text representation
|
||||
* Codes can be also binary, octal or decimal
|
||||
* Support for Unicode, UTF-8 and other charsets
|
||||
* Insert and overwrite edit modes
|
||||
* TODO: Searching for text / hexadecimal code with matching highlighting
|
||||
* Searching for text / hexadecimal code with matches highlighting
|
||||
* Support for undo/redo
|
||||
* TODO: Support for files with size up to exabytes
|
||||
* Support for files with size up to exabytes (in partial file mode)
|
||||
|
||||
Compiling
|
||||
---------
|
||||
|
||||
To build this project, Android Studio is recommended.
|
||||
To compile project and build apk you need installed Java JDK 17 or later and run:
|
||||
|
||||
./gradlew assembleRelease
|
||||
|
||||
or on Windows:
|
||||
|
||||
gradlew.bat assembleRelease
|
||||
|
||||
To modify this project, Android Studio is recommended.
|
||||
https://developer.android.com/studio/
|
||||
|
||||
When using Android Studio modify `playStore = false` line in build.gradle file to avoid legacy errors:
|
||||
|
||||
playStore = true
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
||||
+65
-17
@@ -2,45 +2,93 @@ plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
def playStore = rootProject.ext.playstore
|
||||
// For support of SDK 14, newer material versions requests fails to build
|
||||
def materialVersion = '1.6.1'
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
namespace 'org.exbin.bined.editor.android'
|
||||
if (playstore) {
|
||||
compileSdk {
|
||||
version = release(35)
|
||||
}
|
||||
} else {
|
||||
compileSdk 35
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.exbin.bined.editor.android"
|
||||
minSdk 27
|
||||
targetSdk 34
|
||||
versionCode 5
|
||||
versionName "0.2.1"
|
||||
|
||||
if (playStore) {
|
||||
minSdk 24
|
||||
materialVersion = '1.13.0'
|
||||
} else {
|
||||
minSdk 14
|
||||
multiDexEnabled true
|
||||
|
||||
// sourceSets {
|
||||
// main.java.srcDirs += 'src/legacy'
|
||||
// }
|
||||
}
|
||||
|
||||
targetSdk 35
|
||||
versionCode 19
|
||||
versionName "0.2.10"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
vectorDrawables.generatedDensities = []
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
minifyEnabled true
|
||||
// shrinkResources false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'libsrc'
|
||||
}
|
||||
namespace 'org.exbin.bined.editor.android'
|
||||
|
||||
compileOptions {
|
||||
// Flag to enable support for the new language APIs
|
||||
// coreLibraryDesugaringEnabled true
|
||||
|
||||
// sourceCompatibility JavaVersion.VERSION_1_8
|
||||
// targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
lint {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
// implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
|
||||
// implementation 'com.github.rustamg:file-dialogs:1.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation "com.google.android.material:material:${materialVersion}"
|
||||
// coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
implementation 'net.java.dev.jna:jna:5.18.1@aar'
|
||||
implementation project(':file_picker')
|
||||
// implementation 'org.exbin.bined:bined-core:0.3.0-SNAPSHOT'
|
||||
// implementation 'org.exbin.bined:bined-operation:0.3.0-SNAPSHOT'
|
||||
implementation 'org.exbin.auxiliary:binary_data:0.2.1'
|
||||
// implementation 'org.exbin.auxiliary:binary_data-paged:0.2.1'
|
||||
// implementation 'org.exbin.auxiliary:binary_data:0.2.1'
|
||||
// implementation 'org.exbin.auxiliary:binary_data-array:0.2.1'
|
||||
// implementation 'org.exbin.auxiliary:binary_data-delta:0.2.1'
|
||||
|
||||
testCompileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation 'androidx.test:core:1.7.0'
|
||||
testImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
testImplementation "androidx.test.espresso:espresso-core:3.7.0";
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
androidTestImplementation "androidx.test:runner:1.7.0";
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for binary data - readable sequence of bytes.
|
||||
* <p>
|
||||
* Provides methods to read whole or part of the data to array or stream.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface BinaryData {
|
||||
|
||||
/**
|
||||
* Returns true if data are empty.
|
||||
*
|
||||
* @return true if data empty
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns size of data or -1 if size is not available.
|
||||
*
|
||||
* @return size of data in bytes
|
||||
*/
|
||||
long getDataSize();
|
||||
|
||||
/**
|
||||
* Returns byte data on the given position.
|
||||
*
|
||||
* @param position position
|
||||
* @return byte on requested position
|
||||
*/
|
||||
byte getByte(long position);
|
||||
|
||||
/**
|
||||
* Creates copy of all data.
|
||||
*
|
||||
* @return copy of data
|
||||
*/
|
||||
@Nonnull
|
||||
BinaryData copy();
|
||||
|
||||
/**
|
||||
* Creates copy of given area.
|
||||
*
|
||||
* @param startFrom position to start copy from
|
||||
* @param length length of area
|
||||
* @return copy of data
|
||||
*/
|
||||
@Nonnull
|
||||
BinaryData copy(long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Creates copy of given area into array of bytes.
|
||||
*
|
||||
* @param startFrom position to start copy from
|
||||
* @param target target byte array
|
||||
* @param offset offset position in target
|
||||
* @param length length of area to copy
|
||||
*/
|
||||
void copyToArray(long startFrom, byte[] target, int offset, int length);
|
||||
|
||||
/**
|
||||
* Saves/copies all data to given stream.
|
||||
*
|
||||
* @param outputStream output stream
|
||||
* @throws java.io.IOException if input/output error
|
||||
*/
|
||||
void saveToStream(OutputStream outputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Provides handler for input stream generation.
|
||||
*
|
||||
* @return new instance of input stream
|
||||
*/
|
||||
@Nonnull
|
||||
InputStream getDataInputStream();
|
||||
|
||||
/**
|
||||
* Disposes all allocated data if possible.
|
||||
*/
|
||||
void dispose();
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Input stream for binary data.
|
||||
* <p>
|
||||
* Can process expanding data source.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BinaryDataInputStream extends InputStream implements SeekableStream {
|
||||
|
||||
@Nonnull
|
||||
protected final BinaryData data;
|
||||
protected long position = 0;
|
||||
protected long mark = 0;
|
||||
|
||||
public BinaryDataInputStream(BinaryData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position > data.getDataSize()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
return data.getByte(position++) & 0xFF;
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] output, int off, int len) throws IOException {
|
||||
if (output.length == 0 || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dataSize = data.getDataSize();
|
||||
if (position > dataSize - len) {
|
||||
if (position >= dataSize) {
|
||||
return -1;
|
||||
}
|
||||
len = (int) (dataSize - position);
|
||||
}
|
||||
|
||||
data.copyToArray(position, output, off, len);
|
||||
position += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
position = data.getDataSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) (data.getDataSize() - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public long getProcessedSize() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
position = mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
long dataSize = data.getDataSize();
|
||||
if (position + n < dataSize) {
|
||||
position += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
long skipped = dataSize - position;
|
||||
position = dataSize;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Output stream for binary data.
|
||||
* <p>
|
||||
* Data are expanded as needed.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BinaryDataOutputStream extends OutputStream implements SeekableStream {
|
||||
|
||||
@Nonnull
|
||||
protected final EditableBinaryData data;
|
||||
protected long position = 0;
|
||||
|
||||
public BinaryDataOutputStream(EditableBinaryData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int value) throws IOException {
|
||||
long dataSize = data.getDataSize();
|
||||
if (position == dataSize) {
|
||||
dataSize++;
|
||||
data.insertUninitialized(position, 1);
|
||||
}
|
||||
|
||||
data.setByte(position++, (byte) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] input, int off, int len) throws IOException {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long dataSize = data.getDataSize();
|
||||
if (position + len > dataSize) {
|
||||
long expand = position + len - dataSize;
|
||||
data.insertUninitialized(position, expand);
|
||||
}
|
||||
|
||||
data.replace(position, input, off, len);
|
||||
position += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
if (position > data.getDataSize()) {
|
||||
data.setDataSize(position);
|
||||
}
|
||||
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long getProcessedSize() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
position = data.getDataSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Input stream for binary data range.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BinaryDataRangeInputStream extends InputStream implements SeekableStream, FinishableStream {
|
||||
|
||||
@Nonnull
|
||||
protected final BinaryData data;
|
||||
protected final long startPosition;
|
||||
protected final long length;
|
||||
protected long position = 0;
|
||||
protected long mark = 0;
|
||||
|
||||
public BinaryDataRangeInputStream(BinaryData data) {
|
||||
this.data = data;
|
||||
this.startPosition = 0;
|
||||
this.position = 0;
|
||||
this.length = data.getDataSize();
|
||||
}
|
||||
|
||||
public BinaryDataRangeInputStream(BinaryData data, DataRange dataRange) {
|
||||
this(data, dataRange.startPosition, dataRange.getLength());
|
||||
}
|
||||
|
||||
public BinaryDataRangeInputStream(BinaryData data, long startPosition, long length) {
|
||||
if (startPosition < 0) {
|
||||
throw new IllegalArgumentException("Negative position not allowed");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length not allowed");
|
||||
}
|
||||
long sourceDataSize = data.getDataSize();
|
||||
if (startPosition + length > sourceDataSize) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.startPosition = startPosition;
|
||||
this.position = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position > startPosition + length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
return data.getByte(position++) & 0xFF;
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] output, int off, int len) throws IOException {
|
||||
if (output.length == 0 || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position > startPosition + length - len) {
|
||||
if (position >= startPosition + length) {
|
||||
return -1;
|
||||
}
|
||||
len = (int) (startPosition + length - position);
|
||||
}
|
||||
|
||||
data.copyToArray(position, output, off, len);
|
||||
position += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) ((startPosition + length) - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0 || position > length) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
this.position = startPosition + position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long finish() throws IOException {
|
||||
position = startPosition + length;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProcessedSize() {
|
||||
return position - startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
position = mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
if (position - startPosition + n < length) {
|
||||
position += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
long skipped = startPosition + length - position;
|
||||
position = startPosition + length;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Output stream for binary data.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BinaryDataRengeOutputStream extends OutputStream implements SeekableStream, FinishableStream {
|
||||
|
||||
@Nonnull
|
||||
protected final EditableBinaryData data;
|
||||
protected final long startPosition;
|
||||
protected final long length;
|
||||
protected long position = 0;
|
||||
|
||||
public BinaryDataRengeOutputStream(EditableBinaryData data) {
|
||||
this.data = data;
|
||||
this.startPosition = 0;
|
||||
this.length = data.getDataSize();
|
||||
}
|
||||
|
||||
public BinaryDataRengeOutputStream(EditableBinaryData data, DataRange dataRange) {
|
||||
this(data, dataRange.startPosition, dataRange.getLength());
|
||||
}
|
||||
|
||||
public BinaryDataRengeOutputStream(EditableBinaryData data, long startPosition, long length) {
|
||||
if (startPosition < 0) {
|
||||
throw new IllegalArgumentException("Negative position not allowed");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length not allowed");
|
||||
}
|
||||
long sourceDataSize = data.getDataSize();
|
||||
if (startPosition + length > sourceDataSize) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.startPosition = startPosition;
|
||||
this.position = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int value) throws IOException {
|
||||
if (position >= startPosition + length) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
data.setByte(position++, (byte) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] input, int off, int len) throws IOException {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position + len > startPosition + length) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
data.replace(position, input, off, len);
|
||||
position += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0 || position > length) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
this.position = startPosition + position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProcessedSize() {
|
||||
return position - startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long finish() throws IOException {
|
||||
position = startPosition + length;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Exception for data reading or writing when range of data is not present.
|
||||
* <p>
|
||||
* Exception can optionally provide information about range of data which are
|
||||
* not present, but it doesn't have to be full segment.
|
||||
*/
|
||||
public class DataNotPresentException extends RuntimeException {
|
||||
|
||||
@Nullable
|
||||
protected final DataRange dataRange;
|
||||
|
||||
public DataNotPresentException() {
|
||||
dataRange = null;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable DataRange dataRange) {
|
||||
this.dataRange = dataRange;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable String message) {
|
||||
super(message);
|
||||
dataRange = null;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable String message, @Nullable DataRange dataRange) {
|
||||
super(message);
|
||||
this.dataRange = dataRange;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
dataRange = null;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable Throwable cause) {
|
||||
super(cause);
|
||||
dataRange = null;
|
||||
}
|
||||
|
||||
public DataNotPresentException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
dataRange = null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Optional<DataRange> getDataRange() {
|
||||
return Optional.ofNullable(dataRange);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Exception for overflow situation where more data is inserted/added than it is
|
||||
* allowed to handle.
|
||||
*/
|
||||
public class DataOverflowException extends RuntimeException {
|
||||
|
||||
public DataOverflowException() {
|
||||
}
|
||||
|
||||
public DataOverflowException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DataOverflowException(@Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DataOverflowException(@Nullable Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public DataOverflowException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Simple representation of data range.
|
||||
* <p>
|
||||
* Range includes both start and end positions.
|
||||
*/
|
||||
@Immutable
|
||||
public class DataRange {
|
||||
|
||||
protected final long startPosition;
|
||||
protected final long endPosition;
|
||||
|
||||
public DataRange(long startPosition, long endPosition) {
|
||||
if (endPosition > startPosition) {
|
||||
throw new IllegalStateException("Invalid data range");
|
||||
}
|
||||
|
||||
this.startPosition = startPosition;
|
||||
this.endPosition = endPosition;
|
||||
}
|
||||
|
||||
public long getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
public long getEndPosition() {
|
||||
return endPosition;
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return endPosition - startPosition + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for editable binary data - sequence of bytes.
|
||||
* <p>
|
||||
* Provides methods to read/write whole or part of the data to array or stream.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface EditableBinaryData extends BinaryData {
|
||||
|
||||
/**
|
||||
* Sets data size.
|
||||
* <p>
|
||||
* If size is bigger than current size, it will fill it with zeros,
|
||||
* otherwise it will shrink current data.
|
||||
*
|
||||
* @param size target size
|
||||
*/
|
||||
void setDataSize(long size);
|
||||
|
||||
/**
|
||||
* Sets byte data on the given position.
|
||||
*
|
||||
* @param position position
|
||||
* @param value byte value to be set
|
||||
*/
|
||||
void setByte(long position, byte value);
|
||||
|
||||
/**
|
||||
* Inserts data space of given length to given position without setting any
|
||||
* data to it.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param length length of data
|
||||
*/
|
||||
void insertUninitialized(long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Inserts empty data of given length to given position.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param length length of data
|
||||
*/
|
||||
void insert(long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Inserts given data to given position.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param insertedData data to insert
|
||||
*/
|
||||
void insert(long startFrom, byte[] insertedData);
|
||||
|
||||
/**
|
||||
* Inserts given data to given position.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param insertedData data to insert
|
||||
* @param insertedDataOffset inserted data offset
|
||||
* @param insertedDataLength inserted data length
|
||||
*/
|
||||
void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength);
|
||||
|
||||
/**
|
||||
* Inserts given data to given position.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param insertedData data to insert
|
||||
*/
|
||||
void insert(long startFrom, BinaryData insertedData);
|
||||
|
||||
/**
|
||||
* Inserts given data to given position.
|
||||
*
|
||||
* @param startFrom position to insert to
|
||||
* @param insertedData data to insert
|
||||
* @param insertedDataOffset inserted data offset
|
||||
* @param insertedDataLength inserted data length
|
||||
*/
|
||||
void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength);
|
||||
|
||||
/**
|
||||
* Loads data from given stream expecting provided size.
|
||||
* <p>
|
||||
* Preserves original data outside loaded range. Extends data if needed.
|
||||
*
|
||||
* @param startFrom start position to insert data
|
||||
* @param inputStream input stream
|
||||
* @param maximumDataSize size of data to load or -1 for all data
|
||||
* @return length of loaded data
|
||||
* @throws java.io.IOException if input/output error
|
||||
*/
|
||||
long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException;
|
||||
|
||||
/**
|
||||
* Replaces data in given area with provided data.
|
||||
* <p>
|
||||
* If sourceData are the same instance, data are replaced as it would be
|
||||
* copied to buffer first and replaced then.
|
||||
*
|
||||
* @param targetPosition target position to write to
|
||||
* @param replacingData data to read from
|
||||
*/
|
||||
void replace(long targetPosition, BinaryData replacingData);
|
||||
|
||||
/**
|
||||
* Replaces data in given area with provided data.
|
||||
* <p>
|
||||
* If sourceData are the same instance, data are replaced as it would be
|
||||
* copied to buffer first and replaced then.
|
||||
*
|
||||
* @param targetPosition target position to write to
|
||||
* @param replacingData data to read from
|
||||
* @param startFrom position to start copy from
|
||||
* @param length length of data to copy
|
||||
*/
|
||||
void replace(long targetPosition, BinaryData replacingData, long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Replaces data in given area with provided data.
|
||||
* <p>
|
||||
* If sourceData are the same instance, data are replaced as it would be
|
||||
* copied to buffer first and replaced then.
|
||||
*
|
||||
* @param targetPosition target position to write to
|
||||
* @param replacingData data to read from
|
||||
*/
|
||||
void replace(long targetPosition, byte[] replacingData);
|
||||
|
||||
/**
|
||||
* Replaces data in given area with provided data.
|
||||
* <p>
|
||||
* If sourceData are the same instance, data are replaced as it would be
|
||||
* copied to buffer first and replaced then.
|
||||
*
|
||||
* @param targetPosition target position to write to
|
||||
* @param replacingData data to read from
|
||||
* @param replacingDataOffset position to start copy from
|
||||
* @param length length of data to copy
|
||||
*/
|
||||
void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length);
|
||||
|
||||
/**
|
||||
* Fills given area with empty data.
|
||||
*
|
||||
* @param startFrom position to fill data to
|
||||
* @param length length of area
|
||||
*/
|
||||
void fillData(long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Fills given area with bytes of given value.
|
||||
*
|
||||
* @param startFrom position to fill data to
|
||||
* @param length length of area
|
||||
* @param fill value to fill with
|
||||
*/
|
||||
void fillData(long startFrom, long length, byte fill);
|
||||
|
||||
/**
|
||||
* Removes area of data.
|
||||
*
|
||||
* @param startFrom position to start removal from
|
||||
* @param length length of area
|
||||
*/
|
||||
void remove(long startFrom, long length);
|
||||
|
||||
/**
|
||||
* Removes all existing data.
|
||||
* <p>
|
||||
* Simply releases all references to data pages.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Replaces all data by data red from given stream.
|
||||
*
|
||||
* @param inputStream input stream
|
||||
* @throws java.io.IOException if input/output error
|
||||
*/
|
||||
void loadFromStream(InputStream inputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Provides handler for output stream generation.
|
||||
* <p>
|
||||
* Received data are appended to existing data.
|
||||
*
|
||||
* @return new instance of output stream
|
||||
*/
|
||||
@Nonnull
|
||||
OutputStream getDataOutputStream();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Empty read-only binary data.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
@Immutable
|
||||
public class EmptyBinaryData implements BinaryData {
|
||||
|
||||
private static final class Singleton {
|
||||
|
||||
private static final EmptyBinaryData INSTANCE = new EmptyBinaryData();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static EmptyBinaryData getInstance() {
|
||||
return Singleton.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(long position) {
|
||||
throw new OutOfBoundsException();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BinaryData copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BinaryData copy(long startFrom, long length) {
|
||||
if (startFrom == 0 && length == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
throw new OutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
if (startFrom == 0 && length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new OutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToStream(OutputStream outputStream) throws IOException {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream getDataInputStream() {
|
||||
return new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for finish-able stream - allows to skip/process data to identify
|
||||
* size of remaining data.
|
||||
*/
|
||||
public interface FinishableStream {
|
||||
|
||||
/**
|
||||
* Reads or writes remaining data and returns size of all data processed by
|
||||
* this stream.
|
||||
*
|
||||
* @return size of data in bytes
|
||||
* @throws IOException if input/output error occurs
|
||||
*/
|
||||
long finish() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns size of data processed so far.
|
||||
*
|
||||
* @return size of data in bytes
|
||||
*/
|
||||
long getProcessedSize();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Exception for out of bounds issues with binary data manipulation operations.
|
||||
*/
|
||||
public class OutOfBoundsException extends RuntimeException {
|
||||
|
||||
public OutOfBoundsException() {
|
||||
}
|
||||
|
||||
public OutOfBoundsException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public OutOfBoundsException(@Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public OutOfBoundsException(@Nullable Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public OutOfBoundsException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for seekable stream - allows to change current position.
|
||||
*/
|
||||
public interface SeekableStream {
|
||||
|
||||
/**
|
||||
* Moves position in the stream to given position from the start of the
|
||||
* stream.
|
||||
*
|
||||
* @param position target position
|
||||
* @throws IOException if input/output error
|
||||
*/
|
||||
void seek(long position) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns length of the stream.
|
||||
*
|
||||
* @return length of the stream in bytes, -1 if unable to determine
|
||||
*/
|
||||
long getStreamSize();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data;
|
||||
|
||||
/**
|
||||
* Interface for binary data with missing segments / segments where no data are
|
||||
* present.
|
||||
* <p>
|
||||
* Expected use is to either check presence per position or segments or to read
|
||||
* data and handle presence only when {@link DataNotPresentException} is thrown.
|
||||
*/
|
||||
public interface SparseBinaryData {
|
||||
|
||||
/**
|
||||
* Returns true if data are present on given position.
|
||||
*
|
||||
* @param position position
|
||||
* @return true if present
|
||||
*/
|
||||
boolean isDataPresent(long position);
|
||||
|
||||
/**
|
||||
* Returns closest next subsequent data position where data presence changes
|
||||
* or any position closer, but greater than given position.
|
||||
* <p>
|
||||
* Basic implementation can return position + 1 which forces reading and
|
||||
* handling of {@link DataNotPresentException}.
|
||||
*
|
||||
* @param position position to check from
|
||||
* @return next position to check for presence
|
||||
*/
|
||||
long closestNextPossiblePresenceChange(long position);
|
||||
|
||||
/**
|
||||
* Returns closest previous preceding data position where data presence
|
||||
* changes or any position closer, but lesser than given position.
|
||||
* <p>
|
||||
* Basic implementation can return position - 1 which forces reading and
|
||||
* handling of {@link DataNotPresentException}.
|
||||
*
|
||||
* @param position position to check from
|
||||
* @return previous position to check for presence
|
||||
*/
|
||||
long closestPreviousPossiblePresenceChange(long position);
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.buffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.BinaryDataInputStream;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Implementation of binary data interface using byte buffer.
|
||||
* <p>
|
||||
* To allow parallel reading, read operations must be synchronized due to split
|
||||
* nature of the ByteBuffer position and depending operation.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BufferData implements BinaryData {
|
||||
|
||||
protected static final int BUFFER_SIZE = 4096;
|
||||
|
||||
@Nonnull
|
||||
protected ByteBuffer data;
|
||||
|
||||
public BufferData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance directly wrapping provided byte buffer.
|
||||
*
|
||||
* @param data byte buffer
|
||||
*/
|
||||
public BufferData(@Nullable ByteBuffer data) {
|
||||
this.data = data != null ? data : BufferData.this.allocateBuffer(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance setting value to provided byte array.
|
||||
*
|
||||
* @param data byte array
|
||||
*/
|
||||
public BufferData(@Nullable byte[] data) {
|
||||
if (data == null) {
|
||||
this.data = BufferData.this.allocateBuffer(0);
|
||||
} else {
|
||||
this.data = BufferData.this.allocateBuffer(data.length);
|
||||
this.data.put(data);
|
||||
this.data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
*/
|
||||
public BufferData(int dataSize) {
|
||||
this.data = BufferData.this.allocateBuffer(dataSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns internal data.
|
||||
*
|
||||
* @return byte array
|
||||
*/
|
||||
@Nonnull
|
||||
public ByteBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return data.capacity() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataSize() {
|
||||
return data.capacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized byte getByte(long position) {
|
||||
try {
|
||||
return data.get((int) position);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferData copy() {
|
||||
ByteBuffer copy = allocateBuffer(data.capacity());
|
||||
synchronized (this) {
|
||||
data.rewind();
|
||||
copy.put(data);
|
||||
}
|
||||
return new BufferData(copy);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferData copy(long startFrom, long length) {
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
throw new OutOfBoundsException("Buffer data is limited by integer length");
|
||||
}
|
||||
if (startFrom + length > data.capacity()) {
|
||||
throw new OutOfBoundsException("Attempt to copy outside of data");
|
||||
}
|
||||
|
||||
ByteBuffer copy = allocateBuffer((int) length);
|
||||
synchronized (this) {
|
||||
data.position((int) startFrom);
|
||||
data.limit((int) (startFrom + length));
|
||||
copy.put(data);
|
||||
data.clear();
|
||||
return new BufferData(copy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
try {
|
||||
data.position((int) startFrom);
|
||||
data.get(target, offset, length);
|
||||
} catch (IndexOutOfBoundsException | BufferUnderflowException | IllegalArgumentException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToStream(OutputStream outputStream) throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int remaining = data.capacity();
|
||||
int position = 0;
|
||||
while (remaining > 0) {
|
||||
int length = remaining > BUFFER_SIZE ? BUFFER_SIZE : remaining;
|
||||
synchronized (this) {
|
||||
data.position(position);
|
||||
data.get(buffer, 0, length);
|
||||
}
|
||||
outputStream.write(buffer, 0, length);
|
||||
position += length;
|
||||
remaining -= length;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream getDataInputStream() {
|
||||
return new BinaryDataInputStream(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return data.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
if (obj instanceof BinaryData) {
|
||||
BinaryData other = (BinaryData) obj;
|
||||
return compareTo(other);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
final BufferData other = (BufferData) obj;
|
||||
synchronized (this) {
|
||||
other.data.rewind();
|
||||
data.rewind();
|
||||
return other.data.compareTo(data) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean compareTo(BinaryData other) {
|
||||
long dataSize = getDataSize();
|
||||
if (other.getDataSize() != dataSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int bufferSize = dataSize > BUFFER_SIZE ? BUFFER_SIZE : (int) dataSize;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int offset = 0;
|
||||
int remain = (int) dataSize;
|
||||
while (remain > 0) {
|
||||
int length = remain > bufferSize ? bufferSize : remain;
|
||||
other.copyToArray(offset, buffer, 0, length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (data.get(offset + i) != buffer[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
offset += length;
|
||||
remain -= length;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected ByteBuffer allocateBuffer(int capacity) {
|
||||
return ByteBuffer.allocate(capacity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.buffer;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidParameterException;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.BinaryDataOutputStream;
|
||||
import org.exbin.auxiliary.binary_data.DataOverflowException;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Implementation of editable binary data interface using byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BufferEditableData extends BufferData implements EditableBinaryData {
|
||||
|
||||
public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 5;
|
||||
|
||||
private static final String WRONG_INSERTION_POSITION_ERROR = "Data can be inserted only inside or at the end";
|
||||
private static final String WRONG_REPLACE_POSITION_ERROR = "Data can be replaced only inside or at the end";
|
||||
private static final String ARRAY_OVERFLOW_ERROR = "Maximum array size overflow";
|
||||
|
||||
public BufferEditableData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
public BufferEditableData(int dataSize) {
|
||||
super(dataSize);
|
||||
}
|
||||
|
||||
public BufferEditableData(@Nullable ByteBuffer data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public BufferEditableData(@Nullable byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSize(long size) {
|
||||
if (size < 0) {
|
||||
throw new InvalidParameterException("Size cannot be negative");
|
||||
}
|
||||
|
||||
int oldSize = data.capacity();
|
||||
if (oldSize != size) {
|
||||
ByteBuffer newData = allocateBuffer((int) size);
|
||||
if (size < oldSize) {
|
||||
if (size > 0) {
|
||||
data.rewind();
|
||||
data.limit((int) size);
|
||||
newData.put(data);
|
||||
data.limit(oldSize);
|
||||
}
|
||||
data = newData;
|
||||
} else {
|
||||
if (oldSize > 0) {
|
||||
data.rewind();
|
||||
newData.put(data);
|
||||
}
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByte(long position, byte value) {
|
||||
try {
|
||||
data.put((int) position, value);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUninitialized(long startFrom, long length) {
|
||||
if (startFrom > data.capacity()) {
|
||||
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
|
||||
}
|
||||
if (length > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
|
||||
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
|
||||
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, long length) {
|
||||
insertUninitialized(startFrom, length);
|
||||
fillData(startFrom, length, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData) {
|
||||
if (startFrom > data.capacity()) {
|
||||
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
|
||||
}
|
||||
if (insertedData.length > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
int length = insertedData.length;
|
||||
if (length > 0) {
|
||||
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
|
||||
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
|
||||
try {
|
||||
newData.position((int) startFrom);
|
||||
newData.put(insertedData);
|
||||
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int length) {
|
||||
if (startFrom > data.capacity()) {
|
||||
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
|
||||
}
|
||||
if (length > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
|
||||
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
|
||||
try {
|
||||
newData.position((int) startFrom);
|
||||
newData.put(insertedData, insertedDataOffset, length);
|
||||
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData) {
|
||||
if (startFrom > data.capacity()) {
|
||||
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
|
||||
}
|
||||
if (insertedData.getDataSize() > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
insert(startFrom, insertedData, 0, insertedData.getDataSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
|
||||
if (startFrom > data.capacity()) {
|
||||
throw new OutOfBoundsException(WRONG_INSERTION_POSITION_ERROR);
|
||||
}
|
||||
if (insertedDataLength > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
long length = insertedDataLength;
|
||||
if (length > 0) {
|
||||
ByteBuffer newData = allocateBuffer((int) (data.capacity() + length));
|
||||
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
|
||||
for (int i = 0; i < length; i++) {
|
||||
newData.position((int) startFrom + i);
|
||||
newData.put(insertedData.getByte(insertedDataOffset + i));
|
||||
}
|
||||
BufferEditableData.put(newData, (int) (startFrom + length), data, (int) startFrom, (int) (data.capacity() - startFrom));
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
|
||||
if (maximumDataSize > MAX_ARRAY_LENGTH - data.capacity()) {
|
||||
throw new DataOverflowException(ARRAY_OVERFLOW_ERROR);
|
||||
}
|
||||
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
while (maximumDataSize == -1 || maximumDataSize > 0) {
|
||||
int toRead = buffer.length;
|
||||
if (maximumDataSize >= 0 && toRead > maximumDataSize) {
|
||||
toRead = (int) maximumDataSize;
|
||||
}
|
||||
int read = inputStream.read(buffer, 0, toRead);
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (read > 0) {
|
||||
output.write(buffer, 0, read);
|
||||
if (maximumDataSize >= 0) {
|
||||
maximumDataSize -= read;
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] newData = output.toByteArray();
|
||||
if (startFrom + newData.length > getDataSize()) {
|
||||
setDataSize(startFrom + newData.length);
|
||||
}
|
||||
replace(startFrom, newData);
|
||||
return newData.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length) {
|
||||
fillData(startFrom, length, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length, byte fill) {
|
||||
if (length > 0) {
|
||||
try {
|
||||
for (int i = (int) startFrom; i < startFrom + length; i++) {
|
||||
data.put(i, fill);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData sourceData) {
|
||||
replace(targetPosition, sourceData, 0, sourceData.getDataSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long replacingLength) {
|
||||
if (targetPosition + replacingLength > getDataSize()) {
|
||||
throw new OutOfBoundsException(WRONG_REPLACE_POSITION_ERROR);
|
||||
}
|
||||
|
||||
while (replacingLength > 0) {
|
||||
setByte(targetPosition, replacingData.getByte(startFrom));
|
||||
targetPosition++;
|
||||
startFrom++;
|
||||
replacingLength--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData) {
|
||||
replace(targetPosition, replacingData, 0, replacingData.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
|
||||
if (targetPosition + length > getDataSize()) {
|
||||
throw new OutOfBoundsException(WRONG_REPLACE_POSITION_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
data.position((int) targetPosition);
|
||||
data.put(replacingData, replacingDataOffset, length);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(long startFrom, long length) {
|
||||
if (startFrom + length > data.capacity()) {
|
||||
throw new OutOfBoundsException("Cannot remove from " + startFrom + " with length " + length);
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
ByteBuffer newData = allocateBuffer((int) (data.capacity() - length));
|
||||
BufferEditableData.put(newData, 0, data, 0, (int) startFrom);
|
||||
BufferEditableData.put(newData, (int) startFrom, data, (int) (startFrom + length), (int) (data.capacity() - startFrom - length));
|
||||
data = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferEditableData copy() {
|
||||
ByteBuffer copy = allocateBuffer(data.capacity());
|
||||
synchronized (this) {
|
||||
data.rewind();
|
||||
copy.put(data);
|
||||
}
|
||||
return new BufferEditableData(copy);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferEditableData copy(long startFrom, long length) {
|
||||
if (startFrom + length > data.capacity()) {
|
||||
throw new OutOfBoundsException("Attempt to copy outside of data");
|
||||
}
|
||||
|
||||
ByteBuffer copy = allocateBuffer((int) length);
|
||||
synchronized (this) {
|
||||
BufferEditableData.put(copy, 0, data, (int) startFrom, (int) length);
|
||||
return new BufferEditableData(copy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
data = allocateBuffer(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromStream(InputStream inputStream) throws IOException {
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int read;
|
||||
do {
|
||||
read = inputStream.read(buffer);
|
||||
if (read > 0) {
|
||||
output.write(buffer, 0, read);
|
||||
}
|
||||
} while (read > 0);
|
||||
byte[] byteArray = output.toByteArray();
|
||||
data = allocateBuffer(byteArray.length);
|
||||
data.put(byteArray);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream getDataOutputStream() {
|
||||
return new BinaryDataOutputStream(this);
|
||||
}
|
||||
|
||||
public static void put(ByteBuffer target, int position, ByteBuffer source, int offset, int length) throws IndexOutOfBoundsException {
|
||||
if (target == source) {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
if (position < offset) {
|
||||
while (length > 0) {
|
||||
int blockSize = length > BUFFER_SIZE ? BUFFER_SIZE : length;
|
||||
source.position(offset);
|
||||
source.get(buffer, 0, blockSize);
|
||||
target.position(position);
|
||||
target.put(buffer, 0, blockSize);
|
||||
offset += blockSize;
|
||||
position += blockSize;
|
||||
length -= blockSize;
|
||||
}
|
||||
} else if (position > offset) {
|
||||
while (length > 0) {
|
||||
int blockSize = length > BUFFER_SIZE ? BUFFER_SIZE : length;
|
||||
source.position(offset + length - blockSize);
|
||||
source.get(buffer, 0, blockSize);
|
||||
target.position(position + length - blockSize);
|
||||
target.put(buffer, 0, blockSize);
|
||||
length -= blockSize;
|
||||
}
|
||||
}
|
||||
target.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
source.position(offset);
|
||||
source.limit(offset + length);
|
||||
target.position(position);
|
||||
target.put(source);
|
||||
target.clear();
|
||||
source.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Implementation of binary data interface using direct byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DirectBufferData extends BufferData {
|
||||
|
||||
public DirectBufferData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance directly wrapping provided byte buffer.
|
||||
*
|
||||
* @param data byte buffer
|
||||
*/
|
||||
public DirectBufferData(@Nullable ByteBuffer data) {
|
||||
super(data != null ? data : DirectBufferData.allocateBufferInt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance setting value to provided byte array.
|
||||
*
|
||||
* @param data byte array
|
||||
*/
|
||||
public DirectBufferData(@Nullable byte[] data) {
|
||||
super(DirectBufferData.allocateBufferInt(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
*/
|
||||
public DirectBufferData(int dataSize) {
|
||||
super(DirectBufferData.allocateBufferInt(dataSize));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected ByteBuffer allocateBuffer(int capacity) {
|
||||
return DirectBufferData.allocateBufferInt(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(int capacity) {
|
||||
return ByteBuffer.allocateDirect(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
|
||||
if (data == null) {
|
||||
return DirectBufferData.allocateBufferInt(0);
|
||||
} else {
|
||||
ByteBuffer buffer = DirectBufferData.allocateBufferInt(data.length);
|
||||
buffer.put(data);
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Implementation of editable binary data interface using direct byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DirectBufferEditableData extends BufferEditableData {
|
||||
|
||||
public DirectBufferEditableData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance directly wrapping provided byte buffer.
|
||||
*
|
||||
* @param data byte buffer
|
||||
*/
|
||||
public DirectBufferEditableData(@Nullable ByteBuffer data) {
|
||||
super(data != null ? data : DirectBufferEditableData.allocateBufferInt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance setting value to provided byte array.
|
||||
*
|
||||
* @param data byte array
|
||||
*/
|
||||
public DirectBufferEditableData(@Nullable byte[] data) {
|
||||
super(DirectBufferEditableData.allocateBufferInt(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
*/
|
||||
public DirectBufferEditableData(int dataSize) {
|
||||
super(DirectBufferEditableData.allocateBufferInt(dataSize));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected ByteBuffer allocateBuffer(int capacity) {
|
||||
return DirectBufferEditableData.allocateBufferInt(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(int capacity) {
|
||||
return ByteBuffer.allocateDirect(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
|
||||
if (data == null) {
|
||||
return DirectBufferEditableData.allocateBufferInt(0);
|
||||
} else {
|
||||
ByteBuffer buffer = DirectBufferEditableData.allocateBufferInt(data.length);
|
||||
buffer.put(data);
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.buffer.paged;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.BinaryDataInputStream;
|
||||
import org.exbin.auxiliary.binary_data.BinaryDataOutputStream;
|
||||
import org.exbin.auxiliary.binary_data.buffer.BufferData;
|
||||
import org.exbin.auxiliary.binary_data.buffer.BufferEditableData;
|
||||
import org.exbin.auxiliary.binary_data.DataOverflowException;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
import org.exbin.auxiliary.binary_data.paged.PagedData;
|
||||
import org.exbin.auxiliary.binary_data.paged.DataPageCreator;
|
||||
|
||||
/**
|
||||
* Paged data stored using byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BufferPagedData implements PagedData {
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 4096;
|
||||
public static final long MAX_DATA_SIZE = Long.MAX_VALUE;
|
||||
|
||||
protected int pageSize = DEFAULT_PAGE_SIZE;
|
||||
@Nonnull
|
||||
protected final List<BufferData> data = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
protected DataPageCreator dataPageCreator = null;
|
||||
|
||||
public BufferPagedData() {
|
||||
}
|
||||
|
||||
public BufferPagedData(DataPageCreator dataPageCreator) {
|
||||
this.dataPageCreator = dataPageCreator;
|
||||
}
|
||||
|
||||
public BufferPagedData(int pageSize) {
|
||||
if (pageSize < 1) {
|
||||
throw new InvalidParameterException("Page size cannot be less than 1");
|
||||
}
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return data.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataSize() {
|
||||
return (data.size() > 1 ? (long) (data.size() - 1) * pageSize : 0) + (!data.isEmpty() ? data.get(data.size() - 1).getDataSize() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSize(long size) {
|
||||
if (size < 0) {
|
||||
throw new InvalidParameterException("Size cannot be negative");
|
||||
}
|
||||
|
||||
long dataSize = getDataSize();
|
||||
if (size > dataSize) {
|
||||
int lastPage = (int) (dataSize / pageSize);
|
||||
int lastPageSize = (int) (dataSize % pageSize);
|
||||
long remaining = size - dataSize;
|
||||
// extend last page
|
||||
if (lastPageSize > 0) {
|
||||
BufferData page = getPage(lastPage);
|
||||
int nextPageSize = remaining + lastPageSize > pageSize ? pageSize : (int) remaining + lastPageSize;
|
||||
BufferData newPage = createNewPage(nextPageSize);
|
||||
page.getData().rewind();
|
||||
newPage.getData().put(page.getData());
|
||||
setPage(lastPage, newPage);
|
||||
remaining -= (nextPageSize - lastPageSize);
|
||||
lastPage++;
|
||||
}
|
||||
|
||||
while (remaining > 0) {
|
||||
int nextPageSize = remaining > pageSize ? pageSize : (int) remaining;
|
||||
data.add(createNewPage(nextPageSize));
|
||||
remaining -= nextPageSize;
|
||||
}
|
||||
} else if (size < dataSize) {
|
||||
int lastPage = (int) (size / pageSize);
|
||||
int lastPageSize = (int) (size % pageSize);
|
||||
// shrink last page
|
||||
if (lastPageSize > 0) {
|
||||
BufferData page = getPage(lastPage);
|
||||
BufferData newPage = createNewPage(lastPageSize);
|
||||
BufferPagedData.put(newPage.getData(), 0, page.getData(), 0, lastPageSize);
|
||||
setPage(lastPage, newPage);
|
||||
lastPage++;
|
||||
}
|
||||
|
||||
for (int pageIndex = data.size() - 1; pageIndex >= lastPage; pageIndex--) {
|
||||
data.remove(pageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(long position) {
|
||||
BufferData page = getPage((int) (position / pageSize));
|
||||
try {
|
||||
return page.getByte(position % pageSize);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByte(long position, byte value) {
|
||||
BufferData page = getPage((int) (position / pageSize));
|
||||
try {
|
||||
page.getData().put((int) (position % pageSize), value);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUninitialized(long startFrom, long length) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length of inserted block must be non-negative");
|
||||
}
|
||||
if (startFrom < 0) {
|
||||
throw new IllegalArgumentException("Position of inserted block must be non-negative");
|
||||
}
|
||||
long dataSize = getDataSize();
|
||||
if (startFrom > dataSize) {
|
||||
throw new OutOfBoundsException("Inserted block must be inside or directly after existing data");
|
||||
}
|
||||
if (length > MAX_DATA_SIZE - getDataSize()) {
|
||||
throw new DataOverflowException("Maximum array size overflow");
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long copyLength = dataSize - startFrom;
|
||||
dataSize = dataSize + length;
|
||||
setDataSize(dataSize);
|
||||
long sourceEnd = dataSize - length;
|
||||
long targetEnd = dataSize;
|
||||
// Backward copy
|
||||
while (copyLength > 0) {
|
||||
BufferData sourcePage;
|
||||
int sourceOffset = (int) (sourceEnd % pageSize);
|
||||
if (sourceOffset == 0) {
|
||||
sourcePage = getPage((int) ((sourceEnd - 1) / pageSize));
|
||||
sourceOffset = (int) sourcePage.getDataSize();
|
||||
} else {
|
||||
sourcePage = getPage((int) (sourceEnd / pageSize));
|
||||
}
|
||||
|
||||
BufferData targetPage;
|
||||
int targetOffset = (int) (targetEnd % pageSize);
|
||||
if (targetOffset == 0) {
|
||||
targetPage = getPage((int) ((targetEnd - 1) / pageSize));
|
||||
targetOffset = (int) targetPage.getDataSize();
|
||||
} else {
|
||||
targetPage = getPage((int) (targetEnd / pageSize));
|
||||
}
|
||||
|
||||
int copySize = Math.min(sourceOffset, targetOffset);
|
||||
if (copySize > copyLength) {
|
||||
copySize = (int) copyLength;
|
||||
}
|
||||
|
||||
BufferPagedData.put(targetPage.getData(), targetOffset - copySize, sourcePage.getData(), sourceOffset - copySize, copySize);
|
||||
copyLength -= copySize;
|
||||
sourceEnd -= copySize;
|
||||
targetEnd -= copySize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, long length) {
|
||||
insertUninitialized(startFrom, length);
|
||||
fillData(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData) {
|
||||
long length = insertedData.getDataSize();
|
||||
insertUninitialized(startFrom, length);
|
||||
replace(startFrom, insertedData, 0, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
|
||||
insertUninitialized(startFrom, insertedDataLength);
|
||||
replace(startFrom, insertedData, insertedDataOffset, insertedDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData) {
|
||||
insert(startFrom, insertedData, 0, insertedData.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
|
||||
if (insertedDataLength <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertUninitialized(startFrom, insertedDataLength);
|
||||
|
||||
while (insertedDataLength > 0) {
|
||||
BufferData targetPage = getPage((int) (startFrom / pageSize));
|
||||
int targetOffset = (int) (startFrom % pageSize);
|
||||
int blockLength = pageSize - targetOffset;
|
||||
if (blockLength > insertedDataLength) {
|
||||
blockLength = insertedDataLength;
|
||||
}
|
||||
|
||||
try {
|
||||
targetPage.getData().position(targetOffset);
|
||||
targetPage.getData().put(insertedData, insertedDataOffset, blockLength);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
insertedDataOffset += blockLength;
|
||||
insertedDataLength -= blockLength;
|
||||
startFrom += blockLength;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
|
||||
if (maximumDataSize > MAX_DATA_SIZE - getDataSize()) {
|
||||
throw new DataOverflowException("Maximum array size overflow");
|
||||
}
|
||||
|
||||
if (startFrom > getDataSize()) {
|
||||
setDataSize(startFrom);
|
||||
}
|
||||
|
||||
long loadedData = 0;
|
||||
int pageOffset = (int) (startFrom % pageSize);
|
||||
byte[] buffer = new byte[pageSize];
|
||||
while (maximumDataSize == -1 || maximumDataSize > 0) {
|
||||
int dataToRead = pageSize - pageOffset;
|
||||
if (maximumDataSize >= 0 && maximumDataSize < dataToRead) {
|
||||
dataToRead = (int) maximumDataSize;
|
||||
}
|
||||
if (pageOffset > 0 && dataToRead > pageOffset) {
|
||||
// Align to data pages
|
||||
dataToRead = pageOffset;
|
||||
}
|
||||
|
||||
int readLength = 0;
|
||||
while (dataToRead > 0) {
|
||||
int read = inputStream.read(buffer, readLength, dataToRead);
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
readLength += read;
|
||||
dataToRead -= read;
|
||||
}
|
||||
|
||||
insert(startFrom, buffer, 0, readLength);
|
||||
startFrom += readLength;
|
||||
if (maximumDataSize >= 0) {
|
||||
maximumDataSize -= readLength;
|
||||
}
|
||||
loadedData += readLength;
|
||||
pageOffset = 0;
|
||||
}
|
||||
return loadedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length) {
|
||||
fillData(startFrom, length, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length, byte fill) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length of filled block must be non-negative");
|
||||
}
|
||||
if (startFrom < 0) {
|
||||
throw new IllegalArgumentException("Position of filler block must be non-negative");
|
||||
}
|
||||
if (startFrom + length > getDataSize()) {
|
||||
throw new OutOfBoundsException("Filled block must be inside existing data");
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (startFrom / pageSize));
|
||||
int pageOffset = (int) (startFrom % pageSize);
|
||||
int fillSize = (int) (page.getDataSize() - pageOffset);
|
||||
if (fillSize > length) {
|
||||
fillSize = (int) length;
|
||||
}
|
||||
ByteBuffer pageBuffer = page.getData();
|
||||
for (int i = pageOffset; i < pageOffset + fillSize; i++) {
|
||||
pageBuffer.put(i, fill);
|
||||
|
||||
}
|
||||
length -= fillSize;
|
||||
startFrom += fillSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferPagedData copy() {
|
||||
BufferPagedData targetData = new BufferPagedData();
|
||||
targetData.insert(0, this);
|
||||
return targetData;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferPagedData copy(long startFrom, long length) {
|
||||
BufferPagedData targetData = new BufferPagedData();
|
||||
targetData.insertUninitialized(0, length);
|
||||
targetData.replace(0, this, startFrom, length);
|
||||
return targetData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (startFrom / pageSize));
|
||||
int pageOffset = (int) (startFrom % pageSize);
|
||||
int copySize = pageSize - pageOffset;
|
||||
if (copySize > length) {
|
||||
copySize = length;
|
||||
}
|
||||
|
||||
page.copyToArray(pageOffset, target, offset, copySize);
|
||||
|
||||
length -= copySize;
|
||||
offset += copySize;
|
||||
startFrom += copySize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(long startFrom, long length) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length of removed block must be non-negative");
|
||||
}
|
||||
if (startFrom < 0) {
|
||||
throw new IllegalArgumentException("Position of removed block must be non-negative");
|
||||
}
|
||||
if (startFrom + length > getDataSize()) {
|
||||
throw new OutOfBoundsException("Removed block must be inside existing data");
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
long dataSize = getDataSize();
|
||||
replace(startFrom, this, startFrom + length, dataSize - startFrom - length);
|
||||
setDataSize(dataSize - length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of pages currently used.
|
||||
*
|
||||
* @return count of pages
|
||||
*/
|
||||
@Override
|
||||
public int getPagesCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns currently used page size.
|
||||
*
|
||||
* @return page size in bytes
|
||||
*/
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data page allowing direct access to it.
|
||||
*
|
||||
* @param pageIndex page index
|
||||
* @return data page
|
||||
*/
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferData getPage(int pageIndex) {
|
||||
try {
|
||||
return data.get(pageIndex);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data page replacing existing page by reference.
|
||||
*
|
||||
* @param pageIndex page index
|
||||
* @param dataPage data page
|
||||
*/
|
||||
@Override
|
||||
public void setPage(int pageIndex, BinaryData dataPage) {
|
||||
if (!(dataPage instanceof BufferData)) {
|
||||
throw new UnsupportedOperationException("Unsupported data page type");
|
||||
}
|
||||
try {
|
||||
data.set(pageIndex, (BufferData) dataPage);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData replacingData) {
|
||||
replace(targetPosition, replacingData, 0, replacingData.getDataSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
|
||||
if (targetPosition + length > getDataSize()) {
|
||||
throw new OutOfBoundsException("Data can be replaced only inside or at the end");
|
||||
}
|
||||
|
||||
if (replacingData instanceof BufferPagedData) {
|
||||
if (replacingData != this || (startFrom > targetPosition) || (startFrom + length < targetPosition)) {
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (targetPosition / pageSize));
|
||||
int offset = (int) (targetPosition % pageSize);
|
||||
|
||||
BufferData sourcePage = ((BufferPagedData) replacingData).getPage((int) (startFrom / ((BufferPagedData) replacingData).getPageSize()));
|
||||
int sourcePageSize = (int) sourcePage.getDataSize();
|
||||
int sourceOffset = (int) (startFrom % ((BufferPagedData) replacingData).getPageSize());
|
||||
|
||||
int copySize = pageSize - offset;
|
||||
if (copySize > sourcePageSize - sourceOffset) {
|
||||
copySize = sourcePageSize - sourceOffset;
|
||||
}
|
||||
if (copySize > length) {
|
||||
copySize = (int) length;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferPagedData.put(page.getData(), offset, sourcePage.getData(), sourceOffset, copySize);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
length -= copySize;
|
||||
targetPosition += copySize;
|
||||
startFrom += copySize;
|
||||
}
|
||||
} else {
|
||||
targetPosition += length - 1;
|
||||
startFrom += length - 1;
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (targetPosition / pageSize));
|
||||
int upTo = (int) (targetPosition % pageSize) + 1;
|
||||
|
||||
BufferData sourcePage = ((BufferPagedData) replacingData).getPage((int) (startFrom / ((BufferPagedData) replacingData).getPageSize()));
|
||||
int sourceUpTo = (int) (startFrom % ((BufferPagedData) replacingData).getPageSize()) + 1;
|
||||
|
||||
int copySize = upTo;
|
||||
if (copySize > sourceUpTo) {
|
||||
copySize = sourceUpTo;
|
||||
}
|
||||
if (copySize > length) {
|
||||
copySize = (int) length;
|
||||
}
|
||||
int offset = upTo - copySize;
|
||||
int sourceOffset = sourceUpTo - copySize;
|
||||
|
||||
BufferPagedData.put(page.getData(), offset, sourcePage.getData(), sourceOffset, copySize);
|
||||
length -= copySize;
|
||||
targetPosition -= copySize;
|
||||
startFrom -= copySize;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (targetPosition / pageSize));
|
||||
int offset = (int) (targetPosition % pageSize);
|
||||
|
||||
int copySize = pageSize - offset;
|
||||
if (copySize > length) {
|
||||
copySize = (int) length;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[copySize];
|
||||
replacingData.copyToArray(startFrom, buffer, 0, copySize);
|
||||
page.getData().position(offset);
|
||||
page.getData().put(buffer);
|
||||
|
||||
length -= copySize;
|
||||
targetPosition += copySize;
|
||||
startFrom += copySize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData) {
|
||||
replace(targetPosition, replacingData, 0, replacingData.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
|
||||
if (targetPosition + length > getDataSize()) {
|
||||
throw new OutOfBoundsException("Data can be replaced only inside or at the end");
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
BufferData page = getPage((int) (targetPosition / pageSize));
|
||||
int offset = (int) (targetPosition % pageSize);
|
||||
|
||||
int copySize = pageSize - offset;
|
||||
if (copySize > length) {
|
||||
copySize = length;
|
||||
}
|
||||
|
||||
try {
|
||||
page.getData().position(offset);
|
||||
page.getData().put(replacingData, replacingDataOffset, copySize);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new OutOfBoundsException(ex);
|
||||
}
|
||||
|
||||
length -= copySize;
|
||||
targetPosition += copySize;
|
||||
replacingDataOffset += copySize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromStream(InputStream inputStream) throws IOException {
|
||||
data.clear();
|
||||
byte[] buffer = new byte[pageSize];
|
||||
int cnt;
|
||||
int offset = 0;
|
||||
while ((cnt = inputStream.read(buffer, offset, buffer.length - offset)) > 0) {
|
||||
if (cnt + offset < pageSize) {
|
||||
offset = offset + cnt;
|
||||
} else {
|
||||
data.add(createNewPage(buffer));
|
||||
buffer = new byte[pageSize];
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
byte[] tail = new byte[offset];
|
||||
System.arraycopy(buffer, 0, tail, 0, offset);
|
||||
data.add(createNewPage(tail));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToStream(OutputStream outputStream) throws IOException {
|
||||
for (BufferData dataPage : data) {
|
||||
dataPage.saveToStream(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream getDataOutputStream() {
|
||||
return new BinaryDataOutputStream(this);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream getDataInputStream() {
|
||||
return new BinaryDataInputStream(this);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected BufferData createNewPage(byte[] pageData) {
|
||||
if (dataPageCreator != null) {
|
||||
EditableBinaryData page = dataPageCreator.createPage(pageData.length);
|
||||
page.replace(0, pageData);
|
||||
return (BufferData) page;
|
||||
}
|
||||
|
||||
return new BufferData(pageData);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected BufferData createNewPage(int pageDataSize) {
|
||||
if (dataPageCreator != null) {
|
||||
return (BufferData) dataPageCreator.createPage(pageDataSize);
|
||||
}
|
||||
|
||||
return new BufferData(pageDataSize);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataPageCreator getDataPageCreator() {
|
||||
return dataPageCreator;
|
||||
}
|
||||
|
||||
public void setDataPageCreator(@Nullable DataPageCreator dataPageCreator) {
|
||||
this.dataPageCreator = dataPageCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
if (obj instanceof BinaryData) {
|
||||
BinaryData other = (BinaryData) obj;
|
||||
long dataSize = getDataSize();
|
||||
if (other.getDataSize() != dataSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int pageIndex = 0;
|
||||
int bufferSize = dataSize > pageSize ? pageSize : (int) dataSize;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int offset = 0;
|
||||
int remain = (int) dataSize;
|
||||
while (remain > 0) {
|
||||
int length = remain > bufferSize ? bufferSize : remain;
|
||||
other.copyToArray(offset, buffer, 0, length);
|
||||
|
||||
BufferData pageData = data.get(pageIndex);
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (pageData.getByte(i) != buffer[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
offset += length;
|
||||
remain -= length;
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
final BufferPagedData other = (BufferPagedData) obj;
|
||||
long dataSize = getDataSize();
|
||||
if (other.getDataSize() != dataSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int pageIndex = 0;
|
||||
int otherPageIndex = 0;
|
||||
long offset = 0;
|
||||
long remain = dataSize;
|
||||
while (remain > 0) {
|
||||
int pageOffset = (int) (offset % pageSize);
|
||||
int otherPageOffset = (int) (offset % other.pageSize);
|
||||
|
||||
int length = remain > pageSize - pageOffset ? pageSize - pageOffset : (int) remain;
|
||||
if (length > other.pageSize - otherPageOffset) {
|
||||
length = other.pageSize - otherPageOffset;
|
||||
}
|
||||
|
||||
BufferData pageData = data.get(pageIndex);
|
||||
BufferData otherPageData = other.data.get(otherPageIndex);
|
||||
int pageTestPos = pageOffset;
|
||||
int otherPageTestPos = otherPageOffset;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (pageData.getByte(pageTestPos) != otherPageData.getByte(otherPageTestPos)) {
|
||||
return false;
|
||||
}
|
||||
pageTestPos++;
|
||||
otherPageTestPos++;
|
||||
}
|
||||
|
||||
offset += length;
|
||||
remain -= length;
|
||||
pageIndex++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getDataSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
private static void put(ByteBuffer target, int position, ByteBuffer source, int offset, int length) throws IndexOutOfBoundsException {
|
||||
BufferEditableData.put(target, position, source, offset, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.delta.list.DoublyLinkedItem;
|
||||
|
||||
/**
|
||||
* Abstract data segment of delta data source.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public abstract class DataSegment implements DoublyLinkedItem<DataSegment> {
|
||||
|
||||
@Nullable
|
||||
private DataSegment previous;
|
||||
@Nullable
|
||||
private DataSegment next;
|
||||
|
||||
public DataSegment() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns start position.
|
||||
*
|
||||
* @return start position
|
||||
*/
|
||||
public abstract long getStartPosition();
|
||||
|
||||
/**
|
||||
* Returns length of this segment in bytes.
|
||||
*
|
||||
* @return length of this segment
|
||||
*/
|
||||
public abstract long getLength();
|
||||
|
||||
/**
|
||||
* Returns detached copy of this segment.
|
||||
*
|
||||
* @return copy of this segment
|
||||
*/
|
||||
@Nonnull
|
||||
public abstract DataSegment copy();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataSegment getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNext(@Nullable DataSegment next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataSegment getPrev() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrev(@Nullable DataSegment previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Data source interface.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface DataSource {
|
||||
|
||||
/**
|
||||
* Returns length of the data.
|
||||
*
|
||||
* @return length of the data
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
long getDataLength() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets data length.
|
||||
*
|
||||
* @param dataLength data length
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
void setDataLength(long dataLength) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns single byte of the data.
|
||||
*
|
||||
* @param position data position
|
||||
* @return byte value
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
byte getByte(long position) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets single byte of the data.
|
||||
*
|
||||
* @param position data position
|
||||
* @param value byte value
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
void setByte(long position, byte value) throws IOException;
|
||||
|
||||
/**
|
||||
* Reads data to buffer.
|
||||
*
|
||||
* @param position data position
|
||||
* @param buffer data buffer
|
||||
* @param offset buffer offset
|
||||
* @param length data length
|
||||
* @return length of data red
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
int read(long position, byte[] buffer, int offset, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes data from buffer.
|
||||
*
|
||||
* @param position data position
|
||||
* @param buffer data buffer
|
||||
* @param offset buffer offset
|
||||
* @param length data length
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
void write(long position, byte[] buffer, int offset, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* Clears caches.
|
||||
*/
|
||||
void clearCache();
|
||||
|
||||
/**
|
||||
* Close data source.
|
||||
*
|
||||
* @throws java.io.IOException input output exception
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
import org.exbin.auxiliary.binary_data.delta.list.DefaultDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* Delta document defined as a sequence of segments.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocument implements EditableBinaryData {
|
||||
|
||||
private final SegmentsRepository repository;
|
||||
private DataSource dataSource;
|
||||
private final DefaultDoublyLinkedList<DataSegment> segments = new DefaultDoublyLinkedList<>();
|
||||
|
||||
private long dataLength = 0;
|
||||
private final DeltaDocumentWindow pointerWindow;
|
||||
private final List<DeltaDocumentChangedListener> changeListeners = new ArrayList<>();
|
||||
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
public DeltaDocument(SegmentsRepository repository, DataSource dataSource) throws IOException {
|
||||
this.repository = repository;
|
||||
this.dataSource = dataSource;
|
||||
dataLength = dataSource.getDataLength();
|
||||
if (dataLength > 0) {
|
||||
DataSegment fullFileSegment = repository.createSourceSegment(dataSource, 0, dataLength);
|
||||
segments.add(fullFileSegment);
|
||||
}
|
||||
pointerWindow = new DeltaDocumentWindow(this);
|
||||
pointerWindow.reset();
|
||||
}
|
||||
|
||||
public DeltaDocument(SegmentsRepository repository) {
|
||||
this.repository = repository;
|
||||
dataLength = 0;
|
||||
pointerWindow = new DeltaDocumentWindow(this);
|
||||
pointerWindow.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for accessing data pages.
|
||||
* <p>
|
||||
* Use only if you know what you are doing.
|
||||
*
|
||||
* @return segments
|
||||
*/
|
||||
@Nonnull
|
||||
public DefaultDoublyLinkedList<DataSegment> getSegments() {
|
||||
return segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns segment starting at or before given position and ending after it.
|
||||
* <p>
|
||||
* Returns null if position is at the end or after then end of the document.
|
||||
*
|
||||
* @param position requested position
|
||||
* @return data segment or null
|
||||
*/
|
||||
@Nullable
|
||||
public DataSegment getSegment(long position) {
|
||||
return pointerWindow.getSegment(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return dataLength == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getDataSize() {
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized byte getByte(long position) {
|
||||
return pointerWindow.getByte(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setByte(long position, byte value) {
|
||||
pointerWindow.setByte(position, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insertUninitialized(long startFrom, long length) {
|
||||
pointerWindow.insertUninitialized(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insert(long startFrom, long length) {
|
||||
pointerWindow.insert(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insert(long startFrom, byte[] insertedData) {
|
||||
pointerWindow.insert(startFrom, insertedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
|
||||
pointerWindow.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insert(long startFrom, BinaryData insertedData) {
|
||||
pointerWindow.insert(startFrom, insertedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
|
||||
pointerWindow.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly inserts segment into given position.
|
||||
*
|
||||
* @param startFrom start position
|
||||
* @param segment inserted segment
|
||||
*/
|
||||
public synchronized void insertSegment(long startFrom, DataSegment segment) {
|
||||
pointerWindow.insertSegment(startFrom, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
|
||||
// TODO optimization later
|
||||
long processed = 0;
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
while (maximumDataSize == -1 || maximumDataSize > 0) {
|
||||
int toRead = BUFFER_SIZE;
|
||||
if (maximumDataSize >= 0 && maximumDataSize < toRead) {
|
||||
toRead = (int) maximumDataSize;
|
||||
}
|
||||
int read = inputStream.read(buffer, 0, toRead);
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
pointerWindow.insert(startFrom, buffer, 0, read);
|
||||
if (maximumDataSize >= 0) {
|
||||
maximumDataSize -= read;
|
||||
}
|
||||
startFrom += read;
|
||||
processed += read;
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void replace(long targetPosition, BinaryData replacingData) {
|
||||
remove(targetPosition, replacingData.getDataSize());
|
||||
insert(targetPosition, replacingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
|
||||
remove(targetPosition, length);
|
||||
insert(targetPosition, replacingData, startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void replace(long targetPosition, byte[] replacingData) {
|
||||
remove(targetPosition, replacingData.length);
|
||||
insert(targetPosition, replacingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
|
||||
remove(targetPosition, length);
|
||||
insert(targetPosition, replacingData, replacingDataOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly replaces segment into given position.
|
||||
*
|
||||
* @param targetPosition target position
|
||||
* @param segment inserted segment
|
||||
*/
|
||||
public synchronized void replaceSegment(long targetPosition, DataSegment segment) {
|
||||
remove(targetPosition, segment.getLength());
|
||||
insertSegment(targetPosition, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void fillData(long startFrom, long length) {
|
||||
fillData(startFrom, length, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void fillData(long startFrom, long length, byte fill) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void remove(long startFrom, long length) {
|
||||
pointerWindow.remove(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
dataLength = 0;
|
||||
segments.clear();
|
||||
pointerWindow.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void dispose() {
|
||||
repository.dropDocument(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void loadFromStream(InputStream stream) throws IOException {
|
||||
clear();
|
||||
DeltaDocumentWindow documentWindow = new DeltaDocumentWindow(this);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
long position = 0;
|
||||
int read;
|
||||
do {
|
||||
read = stream.read(buffer);
|
||||
if (read > 0) {
|
||||
documentWindow.insert(position, buffer, 0, read);
|
||||
position += read;
|
||||
}
|
||||
} while (read >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveToStream(OutputStream stream) throws IOException {
|
||||
DeltaDocumentWindow documentWindow = new DeltaDocumentWindow(this);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
long position = 0;
|
||||
long dataSize = getDataSize();
|
||||
while (position < dataSize) {
|
||||
long remains = dataSize - position;
|
||||
int toProcess = remains < BUFFER_SIZE ? (int) remains : BUFFER_SIZE;
|
||||
documentWindow.copyToArray(position, buffer, 0, toProcess);
|
||||
stream.write(buffer, 0, toProcess);
|
||||
position += toProcess;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized BinaryData copy() {
|
||||
return pointerWindow.copy();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized BinaryData copy(long startFrom, long length) {
|
||||
return pointerWindow.copy(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
// TODO optimization later
|
||||
for (int i = 0; i < length; i++) {
|
||||
target[offset + i] = getByte(startFrom + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized OutputStream getDataOutputStream() {
|
||||
return new DeltaDocumentOutputStream(this);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public synchronized InputStream getDataInputStream() {
|
||||
return new DeltaDocumentInputStream(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setDataSize(long dataSize) {
|
||||
if (dataSize < dataLength) {
|
||||
remove(dataSize, dataLength - dataSize);
|
||||
} else if (dataSize > dataLength) {
|
||||
insert(dataLength, dataSize - dataLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs save to source file.
|
||||
*
|
||||
* @throws java.io.IOException on input/output error
|
||||
*/
|
||||
public synchronized void save() throws IOException {
|
||||
repository.saveDocument(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets cached state - needed after change.
|
||||
*/
|
||||
public synchronized void clearCache() {
|
||||
pointerWindow.reset();
|
||||
}
|
||||
|
||||
/* package */ void setDataLength(long dataSize) {
|
||||
this.dataLength = dataSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns segment starting from given position or copy of part of the
|
||||
* segment starting from given position up to the end of length.
|
||||
*
|
||||
* @param position position
|
||||
* @param length length
|
||||
* @return data segment
|
||||
*/
|
||||
@Nullable
|
||||
public synchronized DataSegment getPartCopy(long position, long length) {
|
||||
return pointerWindow.getPartCopy(position, length);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public SegmentsRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void addChangeListener(DeltaDocumentChangedListener listener) {
|
||||
changeListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeChangeListener(DeltaDocumentChangedListener listener) {
|
||||
changeListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void notifyChangeListeners(DeltaDocumentWindow window) {
|
||||
for (DeltaDocumentChangedListener listener : changeListeners) {
|
||||
listener.dataChanged(window);
|
||||
}
|
||||
}
|
||||
|
||||
public void validatePointerPosition() {
|
||||
pointerWindow.validatePointerPosition();
|
||||
}
|
||||
|
||||
public void validateDocumentSize() {
|
||||
long segmentsSizeSum = 0;
|
||||
DataSegment segment = segments.first();
|
||||
while (segment != null) {
|
||||
segmentsSizeSum += segment.getLength();
|
||||
segment = segment.getNext();
|
||||
}
|
||||
|
||||
if (segmentsSizeSum != getDataSize()) {
|
||||
throw new IllegalStateException("Invalid size " + getDataSize() + " (expected " + segmentsSizeSum + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
validatePointerPosition();
|
||||
validateDocumentSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Listener for delta document data changes.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface DeltaDocumentChangedListener {
|
||||
|
||||
/**
|
||||
* Change in data occurred.
|
||||
*
|
||||
* @param window updated window for reference
|
||||
*/
|
||||
void dataChanged(DeltaDocumentWindow window);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.SeekableStream;
|
||||
|
||||
/**
|
||||
* Delta document input stream.
|
||||
* <p>
|
||||
* Can process expanding document.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocumentInputStream extends InputStream implements SeekableStream {
|
||||
|
||||
@Nonnull
|
||||
private final DeltaDocumentWindow data;
|
||||
private long position = 0;
|
||||
private long mark = 0;
|
||||
|
||||
public DeltaDocumentInputStream(DeltaDocument document) {
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position >= data.getDataSize()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
return data.getByte(position++) & 0xFF;
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] output, int offset, int length) throws IOException {
|
||||
if (output.length == 0 || length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dataSize = data.getDataSize();
|
||||
if (position >= dataSize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (position + length > dataSize) {
|
||||
length = (int) (dataSize - position);
|
||||
}
|
||||
|
||||
data.copyToArray(position, output, offset, length);
|
||||
position += length;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
position = data.getDataSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) (data.getDataSize() - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public long getProcessedSize() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
position = mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
long dataSize = data.getDataSize();
|
||||
if (position + n < dataSize) {
|
||||
position += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
long skipped = dataSize - position;
|
||||
position = dataSize;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
import org.exbin.auxiliary.binary_data.SeekableStream;
|
||||
|
||||
/**
|
||||
* Delta document output stream.
|
||||
* <p>
|
||||
* Data are expanded as needed.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocumentOutputStream extends OutputStream implements SeekableStream {
|
||||
|
||||
@Nonnull
|
||||
private final DeltaDocumentWindow data;
|
||||
private long position = 0;
|
||||
|
||||
public DeltaDocumentOutputStream(DeltaDocument document) {
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int value) throws IOException {
|
||||
long dataSize = data.getDataSize();
|
||||
if (position == dataSize) {
|
||||
dataSize++;
|
||||
data.setDataSize(dataSize);
|
||||
}
|
||||
|
||||
data.setByte(position++, (byte) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] input, int offset, int length) throws IOException {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.insert(position, input, offset, length);
|
||||
position += length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
if (position > data.getDataSize()) {
|
||||
data.setDataSize(position);
|
||||
}
|
||||
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long getProcessedSize() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
position = data.getDataSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.FinishableStream;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
import org.exbin.auxiliary.binary_data.SeekableStream;
|
||||
|
||||
/**
|
||||
* Delta document input stream.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocumentRangeInputStream extends InputStream implements SeekableStream, FinishableStream {
|
||||
|
||||
@Nonnull
|
||||
private final DeltaDocumentWindow data;
|
||||
protected final long startPosition;
|
||||
protected final long length;
|
||||
protected long position = 0;
|
||||
private long mark = 0;
|
||||
|
||||
public DeltaDocumentRangeInputStream(DeltaDocument document) {
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
this.startPosition = 0;
|
||||
this.position = 0;
|
||||
this.length = document.getDataSize();
|
||||
}
|
||||
|
||||
public DeltaDocumentRangeInputStream(DeltaDocument document, long startPosition, long length) {
|
||||
if (startPosition < 0) {
|
||||
throw new IllegalArgumentException("Negative position not allowed");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length not allowed");
|
||||
}
|
||||
long sourceDataSize = document.getDataSize();
|
||||
if (startPosition + length > sourceDataSize) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
this.startPosition = startPosition;
|
||||
this.position = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (position > startPosition + length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
return data.getByte(position++) & 0xFF;
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] output, int offset, int len) throws IOException {
|
||||
if (output.length == 0 || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position > startPosition + length - len) {
|
||||
if (position >= startPosition + length) {
|
||||
return -1;
|
||||
}
|
||||
len = (int) (startPosition + length - position);
|
||||
}
|
||||
|
||||
data.copyToArray(position, output, offset, len);
|
||||
position += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return (int) ((startPosition + length) - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0 || position > length) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
this.position = startPosition + position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long finish() throws IOException {
|
||||
position = startPosition + length;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProcessedSize() {
|
||||
return position - startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
position = mark;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
if (position - startPosition + n < length) {
|
||||
position += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
long skipped = startPosition + length - position;
|
||||
position = startPosition + length;
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.FinishableStream;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
import org.exbin.auxiliary.binary_data.SeekableStream;
|
||||
|
||||
/**
|
||||
* Delta document output stream.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocumentRangeOutputStream extends OutputStream implements SeekableStream, FinishableStream {
|
||||
|
||||
@Nonnull
|
||||
private final DeltaDocumentWindow data;
|
||||
protected final long startPosition;
|
||||
protected final long length;
|
||||
protected long position = 0;
|
||||
|
||||
public DeltaDocumentRangeOutputStream(DeltaDocument document) {
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
this.startPosition = 0;
|
||||
this.length = document.getDataSize();
|
||||
}
|
||||
|
||||
public DeltaDocumentRangeOutputStream(DeltaDocument document, long startPosition, long length) {
|
||||
if (startPosition < 0) {
|
||||
throw new IllegalArgumentException("Negative position not allowed");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length not allowed");
|
||||
}
|
||||
long sourceDataSize = document.getDataSize();
|
||||
if (startPosition + length > sourceDataSize) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
this.data = new DeltaDocumentWindow(document);
|
||||
this.startPosition = startPosition;
|
||||
this.position = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int value) throws IOException {
|
||||
if (position >= startPosition + length) {
|
||||
throw new IOException("Position is outside of available range");
|
||||
}
|
||||
data.setByte(position++, (byte) value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] input, int offset, int len) throws IOException {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position + len > startPosition + length) {
|
||||
throw new OutOfBoundsException("Target area is outside of available data");
|
||||
}
|
||||
|
||||
// TODO replace
|
||||
data.remove(position, len);
|
||||
data.insert(position, input, offset, len);
|
||||
position += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0 || position > length) {
|
||||
throw new OutOfBoundsException("Position is outside of available range");
|
||||
}
|
||||
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamSize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProcessedSize() {
|
||||
return position - startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long finish() throws IOException {
|
||||
position = startPosition + length;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
import org.exbin.auxiliary.binary_data.delta.list.DefaultDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* Access window for delta document.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDocumentWindow {
|
||||
|
||||
@Nonnull
|
||||
private final DeltaDocument document;
|
||||
|
||||
private final DataPointer pointer = new DataPointer();
|
||||
|
||||
public DeltaDocumentWindow(DeltaDocument document) {
|
||||
this.document = document;
|
||||
document.addChangeListener((DeltaDocumentWindow window) -> {
|
||||
if (window != DeltaDocumentWindow.this) {
|
||||
pointer.segment = window.pointer.segment;
|
||||
pointer.position = window.pointer.position;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public long getDataSize() {
|
||||
return document.getDataSize();
|
||||
}
|
||||
|
||||
public byte getByte(long position) {
|
||||
focusSegment(position);
|
||||
|
||||
DataSegment targetSegment = pointer.segment;
|
||||
if (targetSegment == null) {
|
||||
throw new NullPointerException("Missing segment for position " + position);
|
||||
}
|
||||
if (targetSegment instanceof SourceSegment) {
|
||||
return ((SourceSegment) targetSegment).getByte(targetSegment.getStartPosition() + (position - pointer.position));
|
||||
} else {
|
||||
return ((MemorySegment) targetSegment).getByte(targetSegment.getStartPosition() + (position - pointer.position));
|
||||
}
|
||||
}
|
||||
|
||||
public void setByte(long position, byte value) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
focusSegment(position);
|
||||
|
||||
if (pointer.segment instanceof SourceSegment) {
|
||||
if (pointer.position != position) {
|
||||
splitSegment(position);
|
||||
focusSegment(position);
|
||||
}
|
||||
|
||||
DataSegment prev = segments.prevTo(pointer.segment);
|
||||
if (prev instanceof MemorySegment) {
|
||||
repository.setMemoryByte((MemorySegment) prev, prev.getLength(), value);
|
||||
} else {
|
||||
MemorySegment segment = repository.createMemorySegment(repository.openMemorySource(), 0, 0);
|
||||
repository.setMemoryByte(segment, 0, value);
|
||||
segments.addBefore(pointer.segment, segment);
|
||||
}
|
||||
pointer.position++;
|
||||
SourceSegment documentSegment = ((SourceSegment) pointer.segment);
|
||||
if (documentSegment.getLength() == 1) {
|
||||
segments.remove(documentSegment);
|
||||
repository.dropSegment(documentSegment);
|
||||
pointer.position = 0;
|
||||
pointer.segment = null;
|
||||
} else {
|
||||
repository.updateSegment(documentSegment, documentSegment.getStartPosition() + 1, documentSegment.getLength() - 1);
|
||||
}
|
||||
} else {
|
||||
if (pointer.segment == null) {
|
||||
pointer.segment = repository.createMemorySegment(repository.openMemorySource(), 0, 0);
|
||||
segments.add(pointer.segment);
|
||||
}
|
||||
repository.setMemoryByte((MemorySegment) pointer.segment, position - pointer.position, value);
|
||||
}
|
||||
|
||||
if (position >= getDataSize()) {
|
||||
document.setDataLength(position + 1);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insertUninitialized(long startFrom, long length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + length;
|
||||
if (pointer.segment instanceof MemorySegment) {
|
||||
repository.insertUninitializedMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, length);
|
||||
document.setDataLength(targetLength);
|
||||
} else if (pointer.segment != null && pointer.position == startFrom && pointer.segment.getPrev() instanceof MemorySegment
|
||||
&& pointer.segment.getPrev().getStartPosition() + pointer.segment.getPrev().getLength() == ((MemorySegment) pointer.segment.getPrev()).getSource().getDataSize()) {
|
||||
MemorySegment prevSegment = (MemorySegment) pointer.segment.getPrev();
|
||||
prevSegment.getSource().insertUninitialized(prevSegment.getSource().getDataSize(), length);
|
||||
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
|
||||
pointer.position++;
|
||||
document.setDataLength(targetLength);
|
||||
} else if (pointer.segment == null && segments.last() instanceof MemorySegment
|
||||
&& segments.last().getStartPosition() + segments.last().getLength() == ((MemorySegment) segments.last()).getSource().getDataSize()) {
|
||||
MemorySegment prevSegment = (MemorySegment) segments.last();
|
||||
prevSegment.getSource().insertUninitialized(prevSegment.getSource().getDataSize(), length);
|
||||
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
|
||||
pointer.position++;
|
||||
document.setDataLength(targetLength);
|
||||
} else {
|
||||
if (startFrom > pointer.position) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertUninitializedMemoryData(insertedSegment, 0, length);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, length);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insert(long startFrom, long length) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + length;
|
||||
if (pointer.segment instanceof MemorySegment) {
|
||||
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, length);
|
||||
document.setDataLength(targetLength);
|
||||
} else if (pointer.segment != null && pointer.position == startFrom && pointer.segment.getPrev() instanceof MemorySegment
|
||||
&& pointer.segment.getPrev().getStartPosition() + pointer.segment.getPrev().getLength() == ((MemorySegment) pointer.segment.getPrev()).getSource().getDataSize()) {
|
||||
MemorySegment prevSegment = (MemorySegment) pointer.segment.getPrev();
|
||||
prevSegment.getSource().insert(prevSegment.getSource().getDataSize(), length);
|
||||
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
|
||||
pointer.position++;
|
||||
document.setDataLength(targetLength);
|
||||
} else if (pointer.segment == null && segments.last() instanceof MemorySegment
|
||||
&& segments.last().getStartPosition() + segments.last().getLength() == ((MemorySegment) segments.last()).getSource().getDataSize()) {
|
||||
MemorySegment prevSegment = (MemorySegment) segments.last();
|
||||
prevSegment.getSource().insert(prevSegment.getSource().getDataSize(), length);
|
||||
repository.updateSegmentLength(prevSegment, prevSegment.getLength() + length);
|
||||
pointer.position++;
|
||||
document.setDataLength(targetLength);
|
||||
} else {
|
||||
if (startFrom > pointer.position) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertMemoryData(insertedSegment, 0, length);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, length);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insert(long startFrom, byte[] insertedData) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
if (insertedData.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + insertedData.length;
|
||||
if (pointer.segment instanceof MemorySegment) {
|
||||
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
|
||||
document.setDataLength(targetLength);
|
||||
} else {
|
||||
if (startFrom > pointer.position) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertMemoryData(insertedSegment, 0, insertedData);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.length);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + insertedDataLength;
|
||||
if (pointer.segment instanceof MemorySegment) {
|
||||
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
|
||||
document.setDataLength(targetLength);
|
||||
} else {
|
||||
if (startFrom > pointer.position) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertMemoryData(insertedSegment, 0, insertedData);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.length);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insert(long startFrom, BinaryData insertedData) {
|
||||
if (insertedData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + insertedData.getDataSize();
|
||||
if (insertedData instanceof DeltaDocument) {
|
||||
if (pointer.position < startFrom) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
|
||||
// Copy all segments from inserted document
|
||||
DeltaDocument insertedDocument = (DeltaDocument) insertedData;
|
||||
DataSegment segment = insertedDocument.getSegments().first();
|
||||
DataSegment copy = repository.copySegment(segment);
|
||||
DataSegment first = copy;
|
||||
if (pointer.segment == null) {
|
||||
segments.add(copy);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, copy);
|
||||
}
|
||||
DataSegment next = segment.getNext();
|
||||
while (next != null) {
|
||||
DataSegment nextCopy = repository.copySegment(next);
|
||||
segments.addAfter(copy, nextCopy);
|
||||
copy = nextCopy;
|
||||
next = next.getNext();
|
||||
}
|
||||
pointer.segment = first;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.getDataSize());
|
||||
} else if (pointer.segment instanceof MemorySegment) {
|
||||
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData);
|
||||
document.setDataLength(targetLength);
|
||||
} else {
|
||||
if (pointer.position < startFrom) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertMemoryData(insertedSegment, 0, insertedData);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.getDataSize());
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
|
||||
if (insertedDataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
focusSegment(startFrom);
|
||||
long targetLength = document.getDataSize() + insertedDataLength;
|
||||
if (insertedData instanceof DeltaDocument) {
|
||||
if (pointer.position < startFrom) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
|
||||
// Copy all segments from inserted document
|
||||
DeltaDocument insertedDocument = (DeltaDocument) insertedData;
|
||||
|
||||
long position = insertedDataOffset;
|
||||
long length = insertedDataLength;
|
||||
DataSegment segment = insertedDocument.getPartCopy(position, length);
|
||||
position += segment.getLength();
|
||||
length -= segment.getLength();
|
||||
DataSegment first = segment;
|
||||
if (pointer.segment == null) {
|
||||
segments.add(segment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, segment);
|
||||
}
|
||||
DataSegment next = segment.getNext();
|
||||
while (length > 0) {
|
||||
DataSegment nextSegment = insertedDocument.getPartCopy(position, length);
|
||||
position += nextSegment.getLength();
|
||||
length -= nextSegment.getLength();
|
||||
segments.addAfter(segment, nextSegment);
|
||||
segment = nextSegment;
|
||||
next = next.getNext();
|
||||
}
|
||||
pointer.segment = first;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.getDataSize());
|
||||
} /*else if (pointer.segment instanceof MemorySegment) {
|
||||
// TODO can't do that directly, also it breaks
|
||||
repository.insertMemoryData((MemorySegment) pointer.segment, startFrom - pointer.position, insertedData, insertedDataOffset, insertedDataLength);
|
||||
document.setDataLength(targetLength);
|
||||
} */ else {
|
||||
if (pointer.position < startFrom) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
MemorySegment insertedSegment = repository.createMemorySegment();
|
||||
repository.insertMemoryData(insertedSegment, 0, insertedData, insertedDataOffset, insertedDataLength);
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeArea(startFrom, insertedData.getDataSize());
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly inserts segment into given position.
|
||||
*
|
||||
* @param startFrom start position
|
||||
* @param insertedSegment inserted segment
|
||||
*/
|
||||
public void insertSegment(long startFrom, DataSegment insertedSegment) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
long targetLength = document.getDataSize() + insertedSegment.getLength();
|
||||
focusSegment(startFrom);
|
||||
if (pointer.position < startFrom) {
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
}
|
||||
if (pointer.segment == null) {
|
||||
segments.add(insertedSegment);
|
||||
} else {
|
||||
segments.addBefore(pointer.segment, insertedSegment);
|
||||
}
|
||||
pointer.segment = insertedSegment;
|
||||
document.setDataLength(targetLength);
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void remove(long startFrom, long length) {
|
||||
if (startFrom + length > document.getDataSize()) {
|
||||
throw new OutOfBoundsException("Removed area is out of bounds");
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
if (length > 0) {
|
||||
long targetLength = document.getDataSize() - length;
|
||||
focusSegment(startFrom + length);
|
||||
splitSegment(startFrom + length);
|
||||
focusSegment(startFrom);
|
||||
splitSegment(startFrom);
|
||||
focusSegment(startFrom);
|
||||
|
||||
// Save position to return to
|
||||
DataSegment prevSegment = (DataSegment) pointer.segment.getPrev();
|
||||
long prevPointerPosition = prevSegment == null ? 0 : pointer.position - prevSegment.getLength();
|
||||
|
||||
// Drop all segments in given range
|
||||
while (length > 0) {
|
||||
length -= pointer.segment.getLength();
|
||||
DataSegment next = segments.nextTo(pointer.segment);
|
||||
repository.dropSegment(pointer.segment);
|
||||
segments.remove(pointer.segment);
|
||||
pointer.segment = next;
|
||||
}
|
||||
|
||||
// Set pointer position
|
||||
pointer.segment = prevSegment;
|
||||
pointer.position = prevPointerPosition;
|
||||
document.setDataLength(targetLength);
|
||||
tryMergeSegments(startFrom);
|
||||
}
|
||||
document.notifyChangeListeners(this);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
pointer.setPointer(0, document.getSegments().first());
|
||||
}
|
||||
|
||||
public void setDataSize(long dataSize) {
|
||||
document.setDataSize(dataSize);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public BinaryData copy() {
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
DeltaDocument copy = repository.createDocument();
|
||||
copy.setDataLength(getDataSize());
|
||||
for (DataSegment segment : segments) {
|
||||
copy.getSegments().add(repository.copySegment(segment));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public BinaryData copy(long startFrom, long length) {
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
DeltaDocument copy = repository.createDocument();
|
||||
copy.setDataLength(length);
|
||||
focusSegment(startFrom);
|
||||
|
||||
if (length > 0) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
DataSegment segment = pointer.segment;
|
||||
if (segment == null) {
|
||||
throw new NullPointerException("Segment on given position not found");
|
||||
}
|
||||
long offset = startFrom - pointer.position;
|
||||
while (length > 0) {
|
||||
long segmentLength = segment.getLength();
|
||||
long copyLength = segmentLength - offset;
|
||||
if (copyLength > length) {
|
||||
copyLength = length;
|
||||
}
|
||||
|
||||
if (offset == 0 && copyLength == segmentLength) {
|
||||
copy.getSegments().add(repository.copySegment(segment));
|
||||
} else if (segment instanceof MemorySegment) {
|
||||
MemorySegment memorySegment = (MemorySegment) segment;
|
||||
copy.getSegments().add(repository.createMemorySegment(memorySegment.getSource(), memorySegment.getStartPosition() + offset, copyLength));
|
||||
} else {
|
||||
SourceSegment fileSegment = (SourceSegment) segment;
|
||||
copy.getSegments().add(repository.createSourceSegment(fileSegment.getSource(), fileSegment.getStartPosition() + offset, copyLength));
|
||||
}
|
||||
length -= copyLength;
|
||||
offset = 0;
|
||||
segment = segments.nextTo(segment);
|
||||
if (length > 0 && segment == null) {
|
||||
throw new NullPointerException("Unexpected end of segments sequence");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
document.copyToArray(startFrom, target, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits current pointer segment on given absolute position.
|
||||
*
|
||||
* @param position split position
|
||||
*/
|
||||
public void splitSegment(long position) {
|
||||
long pointerPosition = pointer.position;
|
||||
DataSegment pointerSegment = pointer.segment;
|
||||
if (position < pointerPosition || (pointerSegment != null && position > pointerPosition + pointerSegment.getLength())) {
|
||||
throw new IllegalStateException("Split position is out of current segment");
|
||||
}
|
||||
|
||||
if (pointerPosition == position || pointerSegment == null || pointerPosition + pointerSegment.getLength() == position) {
|
||||
// No action needed
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
long firstPartSize = position - pointerPosition;
|
||||
if (pointerSegment instanceof MemorySegment) {
|
||||
MemorySegment memorySegment = (MemorySegment) pointerSegment;
|
||||
MemorySegment newSegment = repository.createMemorySegment(memorySegment.getSource(), memorySegment.getStartPosition() + firstPartSize, memorySegment.getLength() - firstPartSize);
|
||||
repository.updateSegmentLength(memorySegment, firstPartSize);
|
||||
segments.addAfter(pointerSegment, newSegment);
|
||||
} else {
|
||||
SourceSegment fileSegment = (SourceSegment) pointerSegment;
|
||||
SourceSegment newSegment = repository.createSourceSegment(fileSegment.getSource(), fileSegment.getStartPosition() + firstPartSize, fileSegment.getLength() - firstPartSize);
|
||||
repository.updateSegmentLength(fileSegment, firstPartSize);
|
||||
segments.addAfter(fileSegment, newSegment);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataSegment getSegment(long position) {
|
||||
focusSegment(position);
|
||||
return pointer.segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns segment starting from given position or copy of part of the
|
||||
* segment starting from given position up to the end of length.
|
||||
*
|
||||
* @param position position
|
||||
* @param length length
|
||||
* @return data segment or null
|
||||
*/
|
||||
@Nullable
|
||||
public DataSegment getPartCopy(long position, long length) {
|
||||
focusSegment(position);
|
||||
if (pointer.segment == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
long offset = position - pointer.position;
|
||||
long partLength = length;
|
||||
if (pointer.segment.getLength() - offset < partLength) {
|
||||
partLength = pointer.segment.getLength() - offset;
|
||||
}
|
||||
return repository.copySegment(pointer.segment, offset, partLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses segment starting at or before given position and ending after it.
|
||||
* <p>
|
||||
* Returns null if position is at the end of the document and throws out of
|
||||
* bounds exception otherwise.
|
||||
*
|
||||
* @param position requested position
|
||||
* @throws OutOfBoundsException if position is before or after document
|
||||
*/
|
||||
private void focusSegment(long position) {
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
long dataSize = getDataSize();
|
||||
if (position == 0) {
|
||||
pointer.position = 0;
|
||||
pointer.segment = segments.first();
|
||||
return;
|
||||
} else if (position == dataSize) {
|
||||
pointer.position = dataSize;
|
||||
pointer.segment = null;
|
||||
return;
|
||||
} else if (position < 0 || position > dataSize) {
|
||||
throw new OutOfBoundsException("Position index out of range");
|
||||
}
|
||||
|
||||
if (pointer.segment == null) {
|
||||
pointer.segment = segments.last();
|
||||
pointer.position = dataSize - pointer.segment.getLength();
|
||||
}
|
||||
|
||||
if (position < pointer.position) {
|
||||
if (pointer.segment == null && position == dataSize) {
|
||||
pointer.segment = segments.last();
|
||||
if (pointer.segment == null) {
|
||||
throwNullSegmentException();
|
||||
}
|
||||
pointer.position -= pointer.segment.getLength();
|
||||
}
|
||||
|
||||
while (position < pointer.position) {
|
||||
pointer.segment = segments.prevTo(pointer.segment);
|
||||
if (pointer.segment == null) {
|
||||
throwNullSegmentException();
|
||||
}
|
||||
pointer.position -= pointer.segment.getLength();
|
||||
}
|
||||
} else {
|
||||
if (pointer.segment == null) {
|
||||
throwNullSegmentException();
|
||||
}
|
||||
|
||||
while (position >= pointer.position + pointer.segment.getLength()) {
|
||||
if ((pointer.segment.getNext() == null) && (position == pointer.position + pointer.segment.getLength())) {
|
||||
break;
|
||||
}
|
||||
pointer.position += pointer.segment.getLength();
|
||||
pointer.segment = segments.nextTo(pointer.segment);
|
||||
if (pointer.segment == null) {
|
||||
throwNullSegmentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryMergeArea(long position, long length) {
|
||||
tryMergeSegments(position);
|
||||
// TODO
|
||||
tryMergeSegments(position + length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to merge segments at specified position.
|
||||
*
|
||||
* @param position target position
|
||||
*/
|
||||
private boolean tryMergeSegments(long position) {
|
||||
if (position == 0 || position >= getDataSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefaultDoublyLinkedList<DataSegment> segments = document.getSegments();
|
||||
SegmentsRepository repository = document.getRepository();
|
||||
focusSegment(position);
|
||||
DataSegment nextSegment = pointer.segment;
|
||||
focusSegment(position - 1);
|
||||
DataSegment segment = pointer.segment;
|
||||
if (segment == nextSegment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (segment instanceof SourceSegment && nextSegment instanceof SourceSegment) {
|
||||
if (((SourceSegment) segment).getStartPosition() + segment.getLength() == ((SourceSegment) nextSegment).getStartPosition()) {
|
||||
repository.updateSegmentLength(segment, segment.getLength() + nextSegment.getLength());
|
||||
repository.dropSegment(nextSegment);
|
||||
segments.remove(nextSegment);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (segment instanceof MemorySegment && nextSegment instanceof MemorySegment) {
|
||||
MemorySegment memorySegment = (MemorySegment) segment;
|
||||
MemorySegment nextMemorySegment = (MemorySegment) nextSegment;
|
||||
if (memorySegment.getSource() == nextMemorySegment.getSource()) {
|
||||
if (memorySegment.getStartPosition() + segment.getLength() == nextMemorySegment.getStartPosition()) {
|
||||
repository.updateSegmentLength(memorySegment, segment.getLength() + nextSegment.getLength());
|
||||
repository.dropSegment(nextSegment);
|
||||
segments.remove(nextSegment);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO join two single memory segments?
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void validatePointerPosition() {
|
||||
if (pointer.segment == null) {
|
||||
if (pointer.position > 0 && pointer.position < getDataSize()) {
|
||||
throw new IllegalStateException("Illegal pointer position " + pointer.position + " for null segment");
|
||||
}
|
||||
} else {
|
||||
long segmentsLengthSum = 0;
|
||||
DataSegment segment = pointer.segment.getPrev();
|
||||
while (segment != null) {
|
||||
segmentsLengthSum += segment.getLength();
|
||||
segment = segment.getPrev();
|
||||
}
|
||||
|
||||
if (segmentsLengthSum != pointer.position) {
|
||||
throw new IllegalStateException("Illegal pointer position " + pointer.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwNullSegmentException() {
|
||||
throw new IllegalStateException("Unexpected null segment");
|
||||
}
|
||||
|
||||
/**
|
||||
* POJO structure for sliding data pointer.
|
||||
*/
|
||||
private static class DataPointer {
|
||||
|
||||
long position;
|
||||
@Nullable
|
||||
DataSegment segment;
|
||||
|
||||
void setPointer(long position, @Nullable DataSegment segment) {
|
||||
this.position = position;
|
||||
this.segment = segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
|
||||
/**
|
||||
* Data source for binary data stored in memory.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class MemoryDataSource implements EditableBinaryData {
|
||||
|
||||
@Nonnull
|
||||
private final EditableBinaryData data;
|
||||
|
||||
public MemoryDataSource(EditableBinaryData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataSize(long size) {
|
||||
data.setDataSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByte(long position, byte value) {
|
||||
data.setByte(position, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, long length) {
|
||||
data.insert(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData) {
|
||||
data.insert(startFrom, insertedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData) {
|
||||
data.insert(startFrom, insertedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(long startFrom, long length) {
|
||||
data.remove(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return data.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataSize() {
|
||||
return data.getDataSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(long position) {
|
||||
return data.getByte(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToStream(OutputStream outputStream) throws IOException {
|
||||
data.saveToStream(outputStream);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BinaryData copy() {
|
||||
return data.copy();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public BinaryData copy(long startFrom, long length) {
|
||||
return data.copy(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUninitialized(long startFrom, long length) {
|
||||
data.insertUninitialized(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, byte[] insertedData, int insertedDataOffset, int insertedDataLength) {
|
||||
data.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(long startFrom, BinaryData insertedData, long insertedDataOffset, long insertedDataLength) {
|
||||
data.insert(startFrom, insertedData, insertedDataOffset, insertedDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long insert(long startFrom, InputStream inputStream, long maximumDataSize) throws IOException {
|
||||
return data.insert(startFrom, inputStream, maximumDataSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData replacingData) {
|
||||
data.replace(targetPosition, replacingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, BinaryData replacingData, long startFrom, long length) {
|
||||
data.replace(targetPosition, replacingData, startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData) {
|
||||
data.replace(targetPosition, replacingData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(long targetPosition, byte[] replacingData, int replacingDataOffset, int length) {
|
||||
data.replace(targetPosition, replacingData, replacingDataOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length) {
|
||||
data.fillData(startFrom, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillData(long startFrom, long length, byte fill) {
|
||||
data.fillData(startFrom, length, fill);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromStream(InputStream inputStream) throws IOException {
|
||||
data.loadFromStream(inputStream);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public OutputStream getDataOutputStream() {
|
||||
return data.getDataOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
|
||||
data.copyToArray(startFrom, target, offset, length);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InputStream getDataInputStream() {
|
||||
return data.getDataInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
data.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Data segment pointing to memory block.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class MemorySegment extends DataSegment {
|
||||
|
||||
@Nonnull
|
||||
private MemoryDataSource source;
|
||||
private long startPosition;
|
||||
private long length;
|
||||
|
||||
public MemorySegment(MemoryDataSource source, long startPosition, long length) {
|
||||
this.source = source;
|
||||
this.startPosition = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MemoryDataSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(MemoryDataSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
public void setStartPosition(long startPosition) {
|
||||
this.startPosition = startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte getByte(long position) {
|
||||
return source.getByte(position);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public DataSegment copy() {
|
||||
return new MemorySegment(null, startPosition, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
|
||||
/**
|
||||
* Memory segment creator.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface MemorySegmentCreator {
|
||||
|
||||
/**
|
||||
* Creates new memory segment.
|
||||
*
|
||||
* @return data page
|
||||
*/
|
||||
@Nonnull
|
||||
EditableBinaryData createSegment();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Data segment pointing to source data.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class SourceSegment extends DataSegment {
|
||||
|
||||
@Nonnull
|
||||
private final DataSource source;
|
||||
private long startPosition;
|
||||
private long length;
|
||||
|
||||
public SourceSegment(DataSource source, long startPosition, long length) {
|
||||
this.source = source;
|
||||
this.startPosition = startPosition;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public DataSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
public void setStartPosition(long startPosition) {
|
||||
this.startPosition = startPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte getByte(long position) {
|
||||
try {
|
||||
return source.getByte(position);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Error while processing data source", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public DataSegment copy() {
|
||||
return new SourceSegment(source, startPosition, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Space placeholder segment.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class SpaceSegment extends DataSegment {
|
||||
|
||||
private long length;
|
||||
|
||||
public SpaceSegment(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartPosition() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setStartPosition(long startPosition) {
|
||||
throw new IllegalStateException("Unable to set start position on space placeholder segment");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte getByte(long position) {
|
||||
throw new IllegalStateException("Unable to read data from space placeholder segment");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public DataSegment copy() {
|
||||
return new SpaceSegment(length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta.file;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Access window for delta data.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DeltaDataPageWindow {
|
||||
|
||||
public static final int PAGE_SIZE = 1024;
|
||||
|
||||
@Nonnull
|
||||
private final FileDataSource data;
|
||||
@Nonnull
|
||||
private final DataPage[] dataPages = new DataPage[]{new DataPage(), new DataPage()};
|
||||
private int activeDataPage = 1;
|
||||
|
||||
public DeltaDataPageWindow(FileDataSource data) {
|
||||
this.data = data;
|
||||
dataPages[0].pageIndex = 0;
|
||||
loadPage(0);
|
||||
data.addCacheClearListener(() -> {
|
||||
DeltaDataPageWindow.this.clearCache();
|
||||
});
|
||||
}
|
||||
|
||||
private void loadPage(int index) {
|
||||
long pageIndex = dataPages[index].pageIndex;
|
||||
long pagePosition = pageIndex * PAGE_SIZE;
|
||||
try {
|
||||
long fileLength = data.getDataLength();
|
||||
byte[] page = dataPages[index].page;
|
||||
int offset = 0;
|
||||
int toRead = PAGE_SIZE;
|
||||
if (pagePosition + PAGE_SIZE > fileLength) {
|
||||
toRead = (int) (fileLength - pagePosition);
|
||||
}
|
||||
while (toRead > 0) {
|
||||
int read = data.read(pagePosition, page, offset, toRead);
|
||||
if (read == -1) {
|
||||
throw new IOException("Unexpected read error ");
|
||||
}
|
||||
toRead -= read;
|
||||
offset += read;
|
||||
pagePosition += read;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DeltaDataPageWindow.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public byte getByte(long position) {
|
||||
long targetPageIndex = position / PAGE_SIZE;
|
||||
int index = -1;
|
||||
long pageIndex1 = dataPages[0].pageIndex;
|
||||
long pageIndex2 = dataPages[1].pageIndex;
|
||||
if (pageIndex1 == targetPageIndex) {
|
||||
index = 0;
|
||||
} else if (pageIndex2 == targetPageIndex) {
|
||||
index = 1;
|
||||
}
|
||||
if (index == -1) {
|
||||
DataPage dataPage = dataPages[activeDataPage];
|
||||
dataPage.pageIndex = targetPageIndex;
|
||||
loadPage(activeDataPage);
|
||||
activeDataPage = (activeDataPage + 1) & 1;
|
||||
return dataPage.page[(int) (position % PAGE_SIZE)];
|
||||
}
|
||||
|
||||
return dataPages[index].page[(int) (position % PAGE_SIZE)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears window cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
dataPages[0].pageIndex = -1;
|
||||
dataPages[1].pageIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple structure for data page.
|
||||
*/
|
||||
private static class DataPage {
|
||||
|
||||
long pageIndex = -1;
|
||||
byte[] page;
|
||||
|
||||
public DataPage() {
|
||||
page = new byte[PAGE_SIZE];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.delta.DataSource;
|
||||
|
||||
/**
|
||||
* Data source for access to file resource locking it for exclusive access.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class FileDataSource implements DataSource {
|
||||
|
||||
@Nonnull
|
||||
private final File file;
|
||||
@Nonnull
|
||||
private final RandomAccessFile accessFile;
|
||||
@Nonnull
|
||||
private final DeltaDataPageWindow window;
|
||||
private boolean closed = false;
|
||||
|
||||
private final List<CacheClearListener> listeners = new ArrayList<>();
|
||||
|
||||
public FileDataSource(File sourceFile, EditMode editMode) throws FileNotFoundException, IOException {
|
||||
file = sourceFile;
|
||||
accessFile = new RandomAccessFile(sourceFile, editMode.getFileAccessMode());
|
||||
window = new DeltaDataPageWindow(this);
|
||||
}
|
||||
|
||||
public FileDataSource(File sourceFile) throws FileNotFoundException, IOException {
|
||||
this(sourceFile, EditMode.READ_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDataLength() throws IOException {
|
||||
checkClosed();
|
||||
return accessFile.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataLength(long length) throws IOException {
|
||||
checkClosed();
|
||||
accessFile.setLength(length);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
/* package */ RandomAccessFile getAccessFile() {
|
||||
checkClosed();
|
||||
return accessFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(long position) throws IOException {
|
||||
checkClosed();
|
||||
return window.getByte(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByte(long position, byte value) throws IOException {
|
||||
checkClosed();
|
||||
accessFile.seek(position);
|
||||
accessFile.writeByte(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(long position, byte[] buffer, int offset, int length) throws IOException {
|
||||
accessFile.seek(position);
|
||||
return accessFile.read(buffer, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(long position, byte[] buffer, int offset, int length) throws IOException {
|
||||
accessFile.seek(position);
|
||||
accessFile.write(buffer, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears cache window.
|
||||
*/
|
||||
@Override
|
||||
public void clearCache() {
|
||||
for (CacheClearListener listener : listeners) {
|
||||
listener.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
checkClosed();
|
||||
accessFile.close();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
if (closed) {
|
||||
throw new IllegalStateException("File was already closed");
|
||||
}
|
||||
}
|
||||
|
||||
public void addCacheClearListener(CacheClearListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeCacheClearListener(CacheClearListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public static interface CacheClearListener {
|
||||
|
||||
void clearCache();
|
||||
}
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
public static enum EditMode {
|
||||
READ_WRITE("rw"),
|
||||
READ_ONLY("r");
|
||||
|
||||
@Nonnull
|
||||
private final String fileAccessMode;
|
||||
|
||||
private EditMode(String fileAccessMode) {
|
||||
this.fileAccessMode = fileAccessMode;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getFileAccessMode() {
|
||||
return fileAccessMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta.list;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Default implementation of doubly linked list of items.
|
||||
* @param <T> doubly linked list item
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DefaultDoublyLinkedList<T extends DoublyLinkedItem<T>> implements DoublyLinkedList<T> {
|
||||
|
||||
private T first;
|
||||
|
||||
/* Cached values. */
|
||||
private T last;
|
||||
private int size = 0;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T first() {
|
||||
return first;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T last() {
|
||||
return last;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T nextTo(T item) {
|
||||
return item.getNext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T prevTo(T item) {
|
||||
return item.getPrev();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T get(int index) {
|
||||
T item = first;
|
||||
while (index > 0) {
|
||||
item = nextTo(item);
|
||||
index--;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public T set(int index, T item) {
|
||||
T origItem = first;
|
||||
while (index > 0) {
|
||||
origItem = nextTo(origItem);
|
||||
index--;
|
||||
}
|
||||
|
||||
if (origItem == null) {
|
||||
throw new OutOfBoundsException("No item for index " + index);
|
||||
}
|
||||
|
||||
T itemPrev = origItem.getPrev();
|
||||
T itemNext = origItem.getNext();
|
||||
origItem.setNext(null);
|
||||
origItem.setPrev(null);
|
||||
item.setPrev(itemPrev);
|
||||
item.setNext(itemNext);
|
||||
if (last == origItem) {
|
||||
last = item;
|
||||
}
|
||||
|
||||
return origItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object item) {
|
||||
T checkedItem = first;
|
||||
while (checkedItem != null) {
|
||||
if (checkedItem.equals(item)) {
|
||||
return true;
|
||||
}
|
||||
checkedItem = nextTo(checkedItem);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean containsAll(Collection<?> items) {
|
||||
for (Object item : items) {
|
||||
if (!contains((T) item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
|
||||
private T current = first;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
T result = current;
|
||||
current = result.getNext();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
if (last == null) {
|
||||
return new Object[0];
|
||||
}
|
||||
int count = indexOf(last);
|
||||
Object[] result = new Object[count];
|
||||
int index = 0;
|
||||
T item = first;
|
||||
while (item != null) {
|
||||
result[index] = item;
|
||||
item = nextTo(item);
|
||||
index++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <X> X[] toArray(X[] template) {
|
||||
int count = last == null ? 0 : indexOf(last);
|
||||
X[] result = template.length >= count ? template : Arrays.copyOf(template, count);
|
||||
int index = 0;
|
||||
T item = first;
|
||||
while (item != null) {
|
||||
result[index] = (X) item;
|
||||
item = nextTo(item);
|
||||
index++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T item) {
|
||||
if (last != null) {
|
||||
last.setNext(item);
|
||||
item.setPrev(last);
|
||||
item.setNext(null);
|
||||
last = item;
|
||||
} else {
|
||||
first = item;
|
||||
last = item;
|
||||
item.setNext(null);
|
||||
item.setPrev(null);
|
||||
}
|
||||
|
||||
size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T item) {
|
||||
if (index == 0 && size == 0) {
|
||||
add(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > size) {
|
||||
throw new OutOfBoundsException("Index " + index + " is greater than " + size);
|
||||
} else if (index == size) {
|
||||
last.setNext(item);
|
||||
item.setPrev(last);
|
||||
item.setNext(null);
|
||||
last = item;
|
||||
} else if (index == 0) {
|
||||
first.setPrev(item);
|
||||
item.setNext(first);
|
||||
first = item;
|
||||
} else {
|
||||
T indexItem = Objects.requireNonNull(get(index));
|
||||
T prevItem = Objects.requireNonNull(indexItem.getPrev());
|
||||
item.setPrev(prevItem);
|
||||
item.setNext(indexItem);
|
||||
prevItem.setNext(item);
|
||||
indexItem.setPrev(item);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds item after given listItem.
|
||||
* <p>
|
||||
* ListItem must be part of the list otherwise list will become broken. Item
|
||||
* should not be member of other list
|
||||
*
|
||||
* @param listItem item from the current list
|
||||
* @param item newly added item
|
||||
*/
|
||||
public void addAfter(T listItem, T item) {
|
||||
T listItemNext = listItem.getNext();
|
||||
if (listItemNext == null) {
|
||||
listItem.setNext(item);
|
||||
item.setPrev(listItem);
|
||||
item.setNext(null);
|
||||
last = item;
|
||||
} else {
|
||||
listItem.setNext(item);
|
||||
listItemNext.setPrev(item);
|
||||
item.setPrev(listItem);
|
||||
item.setNext(listItemNext);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
public void addBefore(T targetItem, T item) {
|
||||
T prev = targetItem.getPrev();
|
||||
if (prev == null) {
|
||||
targetItem.setPrev(item);
|
||||
item.setNext(targetItem);
|
||||
item.setPrev(null);
|
||||
first = item;
|
||||
} else {
|
||||
targetItem.setPrev(item);
|
||||
prev.setNext(item);
|
||||
item.setNext(targetItem);
|
||||
item.setPrev(prev);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> elementsToAdd) {
|
||||
boolean result = false;
|
||||
for (T element : elementsToAdd) {
|
||||
result |= add(element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c) {
|
||||
for (T t : c) {
|
||||
add(index, t);
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object searchedItem) {
|
||||
T item = first;
|
||||
int index = 0;
|
||||
while (item != null) {
|
||||
if (item.equals(searchedItem)) {
|
||||
return index;
|
||||
}
|
||||
item = item.getNext();
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public T remove(int index) {
|
||||
T item = get(index);
|
||||
if (item != null) {
|
||||
removeItem(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T item = (T) o;
|
||||
if (item != null) {
|
||||
removeItem(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeItem(T item) {
|
||||
if (item == first) {
|
||||
T itemNext = item.getNext();
|
||||
if (itemNext != null) {
|
||||
itemNext.setPrev(null);
|
||||
} else {
|
||||
last = null;
|
||||
}
|
||||
first = itemNext;
|
||||
} else {
|
||||
T prev = Objects.requireNonNull(item.getPrev());
|
||||
T next = item.getNext();
|
||||
|
||||
prev.setNext(next);
|
||||
if (next != null) {
|
||||
next.setPrev(prev);
|
||||
} else {
|
||||
last = prev;
|
||||
}
|
||||
}
|
||||
|
||||
size--;
|
||||
item.setPrev(null);
|
||||
item.setNext(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
boolean result = false;
|
||||
for (Object item : collection) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T itemList = (T) item;
|
||||
result |= remove(itemList);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> retainList) {
|
||||
boolean changed = false;
|
||||
if (first == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T item = first;
|
||||
do {
|
||||
if (!retainList.contains(item)) {
|
||||
changed = true;
|
||||
T nextItem = item.getNext();
|
||||
removeItem(item);
|
||||
item = nextItem;
|
||||
} else {
|
||||
item = item.getNext();
|
||||
}
|
||||
} while (item != null);
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
first = null;
|
||||
last = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object searchedItem) {
|
||||
if (size > 0) {
|
||||
T item = last;
|
||||
int index = size - 1;
|
||||
while (item != null) {
|
||||
if (item.equals(searchedItem)) {
|
||||
return index;
|
||||
}
|
||||
item = item.getPrev();
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int index) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta.list;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for item of doubly linked list.
|
||||
* @param <T> instance class
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface DoublyLinkedItem<T> {
|
||||
|
||||
/**
|
||||
* Returns next linked item.
|
||||
*
|
||||
* @return next item
|
||||
*/
|
||||
@Nullable
|
||||
T getNext();
|
||||
|
||||
/**
|
||||
* Sets next linked item.
|
||||
*
|
||||
* @param next next item
|
||||
*/
|
||||
void setNext(@Nullable T next);
|
||||
|
||||
/**
|
||||
* Returns previous linked item.
|
||||
*
|
||||
* @return next item
|
||||
*/
|
||||
@Nullable
|
||||
T getPrev();
|
||||
|
||||
/**
|
||||
* Sets previous linked item.
|
||||
*
|
||||
* @param previous previous item
|
||||
*/
|
||||
void setPrev(@Nullable T previous);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.delta.list;
|
||||
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Doubly linked list of items.
|
||||
* @param <T> doubly linked list item
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface DoublyLinkedList<T> extends List<T> {
|
||||
|
||||
/**
|
||||
* Returns first item of the list.
|
||||
*
|
||||
* @return first item
|
||||
*/
|
||||
@Nullable
|
||||
T first();
|
||||
|
||||
/**
|
||||
* Returns last item of the list.
|
||||
*
|
||||
* @return last item
|
||||
*/
|
||||
@Nullable
|
||||
T last();
|
||||
|
||||
/**
|
||||
* Returns item next to given item.
|
||||
*
|
||||
* @param item item
|
||||
* @return next item or null
|
||||
*/
|
||||
@Nullable
|
||||
T nextTo(T item);
|
||||
|
||||
/**
|
||||
* Returns item previous to given item.
|
||||
*
|
||||
* @param item item
|
||||
* @return previous item or null
|
||||
*/
|
||||
@Nullable
|
||||
T prevTo(T item);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.jna;
|
||||
|
||||
import com.sun.jna.Memory;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.buffer.BufferData;
|
||||
|
||||
/**
|
||||
* Implementation of binary data interface using JNA byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class JnaBufferData extends BufferData {
|
||||
|
||||
public JnaBufferData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance directly wrapping provided byte buffer.
|
||||
*
|
||||
* @param data byte buffer
|
||||
*/
|
||||
public JnaBufferData(@Nullable ByteBuffer data) {
|
||||
super(data != null ? data : JnaBufferData.allocateBufferInt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance setting value to provided byte array.
|
||||
*
|
||||
* @param data byte array
|
||||
*/
|
||||
public JnaBufferData(@Nullable byte[] data) {
|
||||
super(JnaBufferData.allocateBufferInt(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
*/
|
||||
public JnaBufferData(int dataSize) {
|
||||
super(JnaBufferData.allocateBufferInt(dataSize));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected ByteBuffer allocateBuffer(int capacity) {
|
||||
return JnaBufferData.allocateBufferInt(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(int capacity) {
|
||||
try {
|
||||
return new Memory(capacity).getByteBuffer(0, capacity);
|
||||
} catch (Throwable tw) {
|
||||
// Fallback to regular byte buffer
|
||||
return ByteBuffer.allocateDirect(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
|
||||
if (data == null) {
|
||||
return JnaBufferData.allocateBufferInt(0);
|
||||
} else {
|
||||
ByteBuffer buffer = JnaBufferData.allocateBufferInt(data.length);
|
||||
buffer.put(data);
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.jna;
|
||||
|
||||
import com.sun.jna.Memory;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.buffer.BufferEditableData;
|
||||
|
||||
/**
|
||||
* Implementation of editable binary data interface using JNA byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class JnaBufferEditableData extends BufferEditableData {
|
||||
|
||||
public JnaBufferEditableData() {
|
||||
this((ByteBuffer) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance directly wrapping provided byte buffer.
|
||||
*
|
||||
* @param data byte buffer
|
||||
*/
|
||||
public JnaBufferEditableData(@Nullable ByteBuffer data) {
|
||||
super(data != null ? data : JnaBufferEditableData.allocateBufferInt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance setting value to provided byte array.
|
||||
*
|
||||
* @param data byte array
|
||||
*/
|
||||
public JnaBufferEditableData(@Nullable byte[] data) {
|
||||
super(JnaBufferEditableData.allocateBufferInt(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates instance with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
*/
|
||||
public JnaBufferEditableData(int dataSize) {
|
||||
super(JnaBufferEditableData.allocateBufferInt(dataSize));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected ByteBuffer allocateBuffer(int capacity) {
|
||||
return JnaBufferEditableData.allocateBufferInt(capacity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(int capacity) {
|
||||
try {
|
||||
return new Memory(capacity).getByteBuffer(0, capacity);
|
||||
} catch (Throwable tw) {
|
||||
// Fallback to regular byte buffer
|
||||
return ByteBuffer.allocateDirect(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ByteBuffer allocateBufferInt(@Nullable byte[] data) {
|
||||
if (data == null) {
|
||||
return JnaBufferEditableData.allocateBufferInt(0);
|
||||
} else {
|
||||
ByteBuffer buffer = JnaBufferEditableData.allocateBufferInt(data.length);
|
||||
buffer.put(data);
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.jna.paged;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
import org.exbin.auxiliary.binary_data.jna.JnaBufferData;
|
||||
import org.exbin.auxiliary.binary_data.buffer.paged.BufferPagedData;
|
||||
import org.exbin.auxiliary.binary_data.paged.DataPageCreator;
|
||||
|
||||
/**
|
||||
* Paged data stored using JNA byte buffer.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class JnaBufferPagedData extends BufferPagedData {
|
||||
|
||||
public JnaBufferPagedData() {
|
||||
}
|
||||
|
||||
public JnaBufferPagedData(DataPageCreator dataPageCreator) {
|
||||
this.dataPageCreator = dataPageCreator;
|
||||
}
|
||||
|
||||
public JnaBufferPagedData(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected JnaBufferData createNewPage(byte[] pageData) {
|
||||
if (dataPageCreator != null) {
|
||||
EditableBinaryData page = dataPageCreator.createPage(pageData.length);
|
||||
page.replace(0, pageData);
|
||||
return (JnaBufferData) page;
|
||||
}
|
||||
|
||||
return new JnaBufferData(pageData);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected JnaBufferData createNewPage(int pageDataSize) {
|
||||
if (dataPageCreator != null) {
|
||||
return (JnaBufferData) dataPageCreator.createPage(pageDataSize);
|
||||
}
|
||||
|
||||
return new JnaBufferData(pageDataSize);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataPageCreator getDataPageCreator() {
|
||||
return dataPageCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataPageCreator(@Nullable DataPageCreator dataPageCreator) {
|
||||
this.dataPageCreator = dataPageCreator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.paged;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Paged data with support for customizable page creator.
|
||||
*/
|
||||
public interface CustomizablePagedData extends PagedData {
|
||||
|
||||
/**
|
||||
* Returns data page creator.
|
||||
*
|
||||
* @return data page creator
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<DataPageCreator> getDataPageCreator();
|
||||
|
||||
/**
|
||||
* Sets data page creator.
|
||||
*
|
||||
* @param dataPageCreator data page creator
|
||||
*/
|
||||
void setDataPageCreator(@Nullable DataPageCreator dataPageCreator);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.paged;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
|
||||
/**
|
||||
* Data page creator.
|
||||
*/
|
||||
public interface DataPageCreator {
|
||||
|
||||
/**
|
||||
* Creates new data page with specified size.
|
||||
*
|
||||
* @param dataSize data size
|
||||
* @return data page
|
||||
*/
|
||||
@Nonnull
|
||||
EditableBinaryData createPage(int dataSize);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.auxiliary.binary_data.paged;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
|
||||
/**
|
||||
* Interface for paged data.
|
||||
* <p>
|
||||
* Data are stored using block of data of the same size. Last page might be
|
||||
* shorter than page size, but not empty.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface PagedData extends EditableBinaryData {
|
||||
|
||||
/**
|
||||
* Returns number of pages currently used.
|
||||
*
|
||||
* @return count of pages
|
||||
*/
|
||||
int getPagesCount();
|
||||
|
||||
/**
|
||||
* Returns currently used page size.
|
||||
*
|
||||
* @return page size in bytes
|
||||
*/
|
||||
int getPageSize();
|
||||
|
||||
/**
|
||||
* Gets data page allowing direct access to it.
|
||||
*
|
||||
* @param pageIndex page index
|
||||
* @return data page
|
||||
*/
|
||||
@Nonnull
|
||||
BinaryData getPage(int pageIndex);
|
||||
|
||||
/**
|
||||
* Sets data page replacing existing page by reference.
|
||||
*
|
||||
* @param pageIndex page index
|
||||
* @param dataPage data page
|
||||
*/
|
||||
void setPage(int pageIndex, BinaryData dataPage);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of cursor caret overlapping modes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum CaretOverlapMode {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,33 +32,34 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
/**
|
||||
* Input stream translation class which converts from input charset to target
|
||||
* charset.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class CharsetStreamTranslator extends InputStream {
|
||||
|
||||
private static final String UNMAPPABLE_CHARACTER_ERROR = "Unmappable character should be handled automatically";
|
||||
private static final String MALFORMED_CHARACTER_ERROR = "Malformed character should be handled automatically";
|
||||
|
||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||
public static final int DEFAULT_MAX_BYTES_PER_CHAR = 8;
|
||||
public static final int BYTE_BUFFER_SIZE = 16;
|
||||
|
||||
@Nonnull
|
||||
private final CharsetEncoder encoder;
|
||||
protected final CharsetEncoder encoder;
|
||||
@Nonnull
|
||||
private final CharsetDecoder decoder;
|
||||
protected final CharsetDecoder decoder;
|
||||
@Nonnull
|
||||
private final InputStream source;
|
||||
protected final InputStream source;
|
||||
|
||||
@Nonnull
|
||||
private final ByteBuffer inputBuffer;
|
||||
protected final ByteBuffer inputBuffer;
|
||||
@Nonnull
|
||||
private final ByteBuffer outputBuffer;
|
||||
protected final ByteBuffer outputBuffer;
|
||||
@Nonnull
|
||||
private final CharBuffer charBuffer;
|
||||
private boolean endOfInput = false;
|
||||
protected final CharBuffer charBuffer;
|
||||
protected boolean endOfInput = false;
|
||||
|
||||
private int maxInputCharSize;
|
||||
private int maxOutputCharSize;
|
||||
protected int maxInputCharSize;
|
||||
protected int maxOutputCharSize;
|
||||
|
||||
public CharsetStreamTranslator(Charset inputCharset, Charset outputCharset, InputStream source, int bufferSize) {
|
||||
this.source = source;
|
||||
@@ -110,7 +111,7 @@ public class CharsetStreamTranslator extends InputStream {
|
||||
}
|
||||
}
|
||||
|
||||
return outputBuffer.get();
|
||||
return outputBuffer.get() & 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,9 +163,9 @@ public class CharsetStreamTranslator extends InputStream {
|
||||
inputBuffer.position(inputBuffer.position() + 1);
|
||||
}
|
||||
} else if (decodeResult.isUnmappable()) {
|
||||
throw new IllegalStateException("Unmappable character should be handled automatically");
|
||||
throw new IllegalStateException(UNMAPPABLE_CHARACTER_ERROR);
|
||||
} else if (decodeResult.isMalformed()) {
|
||||
throw new IllegalStateException("Malformed character should be handled automatically");
|
||||
throw new IllegalStateException(MALFORMED_CHARACTER_ERROR);
|
||||
}
|
||||
} while (charBuffer.position() == 0);
|
||||
|
||||
@@ -182,9 +183,9 @@ public class CharsetStreamTranslator extends InputStream {
|
||||
} else if (encodeResult.isError()) {
|
||||
throw new IllegalStateException("Unexpected error character");
|
||||
} else if (encodeResult.isUnmappable()) {
|
||||
throw new IllegalStateException("Unmappable character should be handled automatically");
|
||||
throw new IllegalStateException(UNMAPPABLE_CHARACTER_ERROR);
|
||||
} else if (encodeResult.isMalformed()) {
|
||||
throw new IllegalStateException("Malformed character should be handled automatically");
|
||||
throw new IllegalStateException(MALFORMED_CHARACTER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,15 +224,15 @@ public class CharsetStreamTranslator extends InputStream {
|
||||
int offset = position;
|
||||
while (toRead > 0) {
|
||||
try {
|
||||
int red = source.read(buffer, offset, toRead);
|
||||
if (red < 0) {
|
||||
int read = source.read(buffer, offset, toRead);
|
||||
if (read < 0) {
|
||||
inputBuffer.limit(offset);
|
||||
endOfInput = true;
|
||||
break;
|
||||
}
|
||||
|
||||
offset += red;
|
||||
toRead -= red;
|
||||
offset += read;
|
||||
toRead -= read;
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CharsetStreamTranslator.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ package org.exbin.bined;
|
||||
* <p>
|
||||
* Used to specify what to do with basic clipboard actions like cut, copy, paste
|
||||
* and delete.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum ClipboardHandlingMode {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Interface for code area caret.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface CodeAreaCaret {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,8 +19,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for code area caret change listener.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CodeAreaCaretListener {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,6 @@ import javax.annotation.Nonnull;
|
||||
/**
|
||||
* Specifies caret position as combination of data position, section and code
|
||||
* offset of code representation.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface CodeAreaCaretPosition {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Code Area control interface.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface CodeAreaControl extends DataProvider {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Code area section interface.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface CodeAreaSection {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,13 @@ package org.exbin.bined;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Selection between two positions where begin represents origin point and end
|
||||
* of the selection can be before or after begin.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class CodeAreaSelection {
|
||||
|
||||
private long start;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,12 +21,11 @@ import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
import org.exbin.auxiliary.binary_data.EditableBinaryData;
|
||||
|
||||
/**
|
||||
* Binary editor component utilities.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
* Binary editor utilities.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class CodeAreaUtils {
|
||||
@@ -36,6 +35,8 @@ public class CodeAreaUtils {
|
||||
private static final int CODE_BUFFER_LENGTH = 16;
|
||||
|
||||
public static final String MIME_CLIPBOARD_BINARY = "application/octet-stream";
|
||||
public static final String BINED_CLIPBOARD_MIME = "application/x-bined";
|
||||
public static final String BINED_CLIPBOARD_MIME_FULL = BINED_CLIPBOARD_MIME + "; class=" + BinaryData.class.getCanonicalName();
|
||||
public static final String CONTENT_DATA_ERROR = "Content data is null";
|
||||
public static final String NULL_FIELD_ERROR = "Field cannot be null";
|
||||
|
||||
@@ -339,7 +340,7 @@ public class CodeAreaUtils {
|
||||
* @param keyValue keyboard key value
|
||||
* @param codeOffset current code offset
|
||||
* @param codeType current code type
|
||||
* @return true if key value value is valid
|
||||
* @return true if key value is valid
|
||||
*/
|
||||
public static boolean isValidCodeKeyValue(char keyValue, int codeOffset, CodeType codeType) {
|
||||
boolean validKey = false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Code area zone identifier.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface CodeAreaZone {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of code characters case.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum CodeCharactersCase {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of supported code types.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum CodeType {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Interface for data change listener.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface DataChangedListener {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package org.exbin.bined;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.exbin.auxiliary.binary_data.BinaryData;
|
||||
|
||||
/**
|
||||
* Data provider.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface DataProvider {
|
||||
|
||||
@@ -30,7 +28,7 @@ public interface DataProvider {
|
||||
*
|
||||
* @return binary data
|
||||
*/
|
||||
@Nullable
|
||||
@Nonnull
|
||||
BinaryData getContentData();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,8 +24,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
/**
|
||||
* Specifies caret position as combination of data position, section and code
|
||||
* offset of code representation.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class DefaultCodeAreaCaretPosition implements CodeAreaCaretPosition {
|
||||
@@ -38,7 +36,7 @@ public class DefaultCodeAreaCaretPosition implements CodeAreaCaretPosition {
|
||||
public DefaultCodeAreaCaretPosition() {
|
||||
}
|
||||
|
||||
public DefaultCodeAreaCaretPosition(long dataPosition, int codeOffset, CodeAreaSection section) {
|
||||
public DefaultCodeAreaCaretPosition(long dataPosition, int codeOffset, @Nullable CodeAreaSection section) {
|
||||
this.dataPosition = dataPosition;
|
||||
this.codeOffset = codeOffset;
|
||||
this.section = section;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of edit modes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum EditMode {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,8 +19,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for edit mode change listener.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface EditModeChangedListener {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of edit operations.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum EditOperation {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of supported position code types.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum PositionCodeType {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Row wrapping mode.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum RowWrappingMode {
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of scrollbar orientation variants.
|
||||
*/
|
||||
public enum ScrollBarOrientation {
|
||||
HORIZONTAL, VERTICAL
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Enumeration of scrollbar visibility modes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum ScrollBarVisibility {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined;
|
||||
|
||||
/**
|
||||
* Interface for scrolling listener.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public interface ScrollingListener {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,9 @@
|
||||
*/
|
||||
package org.exbin.bined;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Interface for selection change listener.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface SelectionChangedListener {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,8 +22,6 @@ import javax.annotation.concurrent.Immutable;
|
||||
/**
|
||||
* Selection range is selection between two positions where begin represents
|
||||
* origin point and end of the selection can be before or after begin.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@Immutable
|
||||
public class SelectionRange {
|
||||
@@ -49,10 +47,10 @@ public class SelectionRange {
|
||||
|
||||
/**
|
||||
* Creates selection range from start to end including the start and not
|
||||
* including the end position.
|
||||
* including character at the end position.
|
||||
*
|
||||
* @param start selection start position
|
||||
* @param end selection end position without actual end position itself
|
||||
* @param end selection end position without character at the end position
|
||||
*/
|
||||
public SelectionRange(long start, long end) {
|
||||
if (start < 0) {
|
||||
@@ -67,10 +65,20 @@ public class SelectionRange {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns selection start position.
|
||||
*
|
||||
* @return selection start position.
|
||||
*/
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns selection end position where selection is not including character at the end position.
|
||||
*
|
||||
* @return selection end position.
|
||||
*/
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Border paint modes for basic painter.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum BasicBackgroundPaintMode {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,8 +26,6 @@ import org.exbin.bined.RowWrappingMode;
|
||||
|
||||
/**
|
||||
* Code area data representation structure for basic variant.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BasicCodeAreaLayout {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,35 +26,33 @@ import org.exbin.bined.capability.BasicScrollingCapable;
|
||||
|
||||
/**
|
||||
* Code area scrolling.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BasicCodeAreaScrolling {
|
||||
|
||||
@Nonnull
|
||||
private final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
|
||||
protected final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
|
||||
@Nonnull
|
||||
private ScrollBarVerticalScale scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
|
||||
private int horizontalExtentDifference;
|
||||
private int verticalExtentDifference;
|
||||
private int horizontalScrollBarHeight;
|
||||
private int verticalScrollBarWidth;
|
||||
protected ScrollBarVerticalScale scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
|
||||
protected int horizontalExtentDifference;
|
||||
protected int verticalExtentDifference;
|
||||
protected int horizontalScrollBarHeight;
|
||||
protected int verticalScrollBarWidth;
|
||||
|
||||
private int lastVerticalScrollingValue = -1;
|
||||
protected int lastVerticalScrollingValue = -1;
|
||||
|
||||
@Nonnull
|
||||
private VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.ROW;
|
||||
protected VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.ROW;
|
||||
@Nonnull
|
||||
private ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
|
||||
protected ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
|
||||
@Nonnull
|
||||
private HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
|
||||
protected HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
|
||||
@Nonnull
|
||||
private ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
|
||||
protected ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
|
||||
@Nonnull
|
||||
private final CodeAreaScrollPosition maximumScrollPosition = new CodeAreaScrollPosition();
|
||||
protected final CodeAreaScrollPosition maximumScrollPosition = new CodeAreaScrollPosition();
|
||||
|
||||
private static final long ROW_POSITION_LIMIT = Long.MAX_VALUE / Integer.MAX_VALUE;
|
||||
protected int maximumScrollBarHeight = Integer.MAX_VALUE;
|
||||
|
||||
@Nullable
|
||||
private Runnable verticalExtentChangeListener = null;
|
||||
@@ -73,8 +71,7 @@ public class BasicCodeAreaScrolling {
|
||||
this.verticalScrollBarWidth = verticalScrollBarWidth;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ScrollViewDimension computeViewDimension(int dataViewWidth, int dataViewHeight, BasicCodeAreaLayout layout, BasicCodeAreaStructure structure, int characterWidth, int rowHeight) {
|
||||
public void computeViewDimension(ScrollViewDimension outputDimension, int dataViewWidth, int dataViewHeight, BasicCodeAreaLayout layout, BasicCodeAreaStructure structure, int characterWidth, int rowHeight) {
|
||||
int charsPerRow = structure.getCharactersPerRow();
|
||||
int dataWidth = layout.computePositionX(charsPerRow, characterWidth);
|
||||
boolean fitsHorizontally = computeFitsHorizontally(dataViewWidth, dataWidth);
|
||||
@@ -105,15 +102,14 @@ public class BasicCodeAreaScrolling {
|
||||
height = recomputeScrollViewHeight(dataViewHeight, rowHeight, rowsPerData);
|
||||
}
|
||||
|
||||
// TODO avoid creation of instance
|
||||
return new ScrollViewDimension(dataViewWidth, dataViewHeight, width, height);
|
||||
outputDimension.setDimension(dataViewWidth, dataViewHeight, width, height);
|
||||
}
|
||||
|
||||
private boolean computeFitsHorizontally(int dataViewWidth, int dataWidth) {
|
||||
protected boolean computeFitsHorizontally(int dataViewWidth, int dataWidth) {
|
||||
return dataWidth <= dataViewWidth;
|
||||
}
|
||||
|
||||
private boolean computeFitsVertically(int dataViewHeight, long rowsPerData, int rowHeight) {
|
||||
protected boolean computeFitsVertically(int dataViewHeight, long rowsPerData, int rowHeight) {
|
||||
int availableRows = (dataViewHeight + rowHeight - 1) / rowHeight;
|
||||
if (rowsPerData > availableRows) {
|
||||
return false;
|
||||
@@ -122,7 +118,7 @@ public class BasicCodeAreaScrolling {
|
||||
return rowsPerData * rowHeight <= dataViewHeight;
|
||||
}
|
||||
|
||||
private int recomputeScrollViewWidth(int dataViewWidth, int characterWidth, int dataWidth, int charsPerRow) {
|
||||
protected int recomputeScrollViewWidth(int dataViewWidth, int characterWidth, int dataWidth, int charsPerRow) {
|
||||
int scrollViewWidth = 0;
|
||||
switch (horizontalScrollUnit) {
|
||||
case PIXEL: {
|
||||
@@ -143,13 +139,13 @@ public class BasicCodeAreaScrolling {
|
||||
return scrollViewWidth;
|
||||
}
|
||||
|
||||
private int recomputeScrollViewHeight(int dataViewHeight, int rowHeight, long rowsPerData) {
|
||||
protected int recomputeScrollViewHeight(int dataViewHeight, int rowHeight, long rowsPerData) {
|
||||
int scrollViewHeight = 0;
|
||||
switch (verticalScrollUnit) {
|
||||
case PIXEL: {
|
||||
if (rowsPerData > Integer.MAX_VALUE / rowHeight) {
|
||||
if (rowsPerData > maximumScrollBarHeight / rowHeight) {
|
||||
scrollBarVerticalScale = ScrollBarVerticalScale.SCALED;
|
||||
scrollViewHeight = Integer.MAX_VALUE;
|
||||
scrollViewHeight = maximumScrollBarHeight;
|
||||
changeVerticalExtentDifference(0);
|
||||
} else {
|
||||
scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
|
||||
@@ -159,9 +155,9 @@ public class BasicCodeAreaScrolling {
|
||||
break;
|
||||
}
|
||||
case ROW: {
|
||||
if (rowsPerData > (Integer.MAX_VALUE - dataViewHeight)) {
|
||||
if (rowsPerData > (maximumScrollBarHeight - dataViewHeight)) {
|
||||
scrollBarVerticalScale = ScrollBarVerticalScale.SCALED;
|
||||
scrollViewHeight = Integer.MAX_VALUE;
|
||||
scrollViewHeight = maximumScrollBarHeight;
|
||||
changeVerticalExtentDifference(0);
|
||||
} else {
|
||||
scrollBarVerticalScale = ScrollBarVerticalScale.NORMAL;
|
||||
@@ -219,7 +215,7 @@ public class BasicCodeAreaScrolling {
|
||||
long rest = rowsPerDocumentToLastPage % maxValue;
|
||||
targetRow += (rest * scrollBarValue) / maxValue;
|
||||
} else {
|
||||
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / Integer.MAX_VALUE;
|
||||
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / maximumScrollBarHeight;
|
||||
}
|
||||
scrollPosition.setRowPosition(targetRow);
|
||||
}
|
||||
@@ -245,7 +241,7 @@ public class BasicCodeAreaScrolling {
|
||||
long rest = rowsPerDocumentToLastPage % maxValue;
|
||||
targetRow += (rest * scrollBarValue) / maxValue;
|
||||
} else {
|
||||
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / Integer.MAX_VALUE;
|
||||
targetRow = (scrollBarValue * rowsPerDocumentToLastPage) / maximumScrollBarHeight;
|
||||
}
|
||||
scrollPosition.setRowPosition(targetRow);
|
||||
}
|
||||
@@ -270,10 +266,10 @@ public class BasicCodeAreaScrolling {
|
||||
case PIXEL: {
|
||||
if (scrollBarVerticalScale == ScrollBarVerticalScale.SCALED) {
|
||||
int scrollValue;
|
||||
if (scrollPosition.getRowPosition() < ROW_POSITION_LIMIT) {
|
||||
scrollValue = (int) ((scrollPosition.getRowPosition() * Integer.MAX_VALUE) / rowsPerDocument);
|
||||
if (scrollPosition.getRowPosition() < Long.MAX_VALUE / maximumScrollBarHeight) {
|
||||
scrollValue = (int) ((scrollPosition.getRowPosition() * maximumScrollBarHeight) / rowsPerDocument);
|
||||
} else {
|
||||
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / Integer.MAX_VALUE));
|
||||
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / maximumScrollBarHeight));
|
||||
}
|
||||
return scrollValue;
|
||||
}
|
||||
@@ -282,10 +278,10 @@ public class BasicCodeAreaScrolling {
|
||||
case ROW: {
|
||||
if (scrollBarVerticalScale == ScrollBarVerticalScale.SCALED) {
|
||||
int scrollValue;
|
||||
if (scrollPosition.getRowPosition() < ROW_POSITION_LIMIT) {
|
||||
scrollValue = (int) ((scrollPosition.getRowPosition() * Integer.MAX_VALUE) / rowsPerDocument);
|
||||
if (scrollPosition.getRowPosition() < Long.MAX_VALUE / maximumScrollBarHeight) {
|
||||
scrollValue = (int) ((scrollPosition.getRowPosition() * maximumScrollBarHeight) / rowsPerDocument);
|
||||
} else {
|
||||
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / Integer.MAX_VALUE));
|
||||
scrollValue = (int) (scrollPosition.getRowPosition() / (rowsPerDocument / maximumScrollBarHeight));
|
||||
}
|
||||
return scrollValue;
|
||||
}
|
||||
@@ -315,6 +311,14 @@ public class BasicCodeAreaScrolling {
|
||||
this.horizontalExtentChangeListener = horizontalExtentChangeListener;
|
||||
}
|
||||
|
||||
public int getMaximumScrollBarHeight() {
|
||||
return maximumScrollBarHeight;
|
||||
}
|
||||
|
||||
public void setMaximumScrollBarHeight(int maximumScrollBarHeight) {
|
||||
this.maximumScrollBarHeight = maximumScrollBarHeight;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public CodeAreaScrollPosition computeScrolling(CodeAreaScrollPosition startPosition, ScrollingDirection direction, int rowsPerPage, long rowsPerDocument) {
|
||||
CodeAreaScrollPosition targetPosition = new CodeAreaScrollPosition();
|
||||
@@ -469,7 +473,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private PositionScrollVisibility checkTopScrollVisibility(long rowPosition) {
|
||||
protected PositionScrollVisibility checkTopScrollVisibility(long rowPosition) {
|
||||
if (verticalScrollUnit == VerticalScrollUnit.ROW) {
|
||||
return rowPosition < scrollPosition.getRowPosition() ? PositionScrollVisibility.NOT_VISIBLE : PositionScrollVisibility.VISIBLE;
|
||||
}
|
||||
@@ -485,7 +489,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private PositionScrollVisibility checkBottomScrollVisibility(long rowPosition, int rowsPerPage, int rowOffset, int rowHeight) {
|
||||
protected PositionScrollVisibility checkBottomScrollVisibility(long rowPosition, int rowsPerPage, int rowOffset, int rowHeight) {
|
||||
int sumOffset = scrollPosition.getRowOffset() + rowOffset;
|
||||
|
||||
long lastFullRow = scrollPosition.getRowPosition() + rowsPerPage;
|
||||
@@ -507,7 +511,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private PositionScrollVisibility checkLeftScrollVisibility(int charsPosition) {
|
||||
protected PositionScrollVisibility checkLeftScrollVisibility(int charsPosition) {
|
||||
int charPos = scrollPosition.getCharPosition();
|
||||
if (horizontalScrollUnit != HorizontalScrollUnit.PIXEL) {
|
||||
return charsPosition < charPos ? PositionScrollVisibility.NOT_VISIBLE : PositionScrollVisibility.VISIBLE;
|
||||
@@ -524,7 +528,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private PositionScrollVisibility checkRightScrollVisibility(int charsPosition, int charsPerPage, int charOffset, int characterWidth) {
|
||||
protected PositionScrollVisibility checkRightScrollVisibility(int charsPosition, int charsPerPage, int charOffset, int characterWidth) {
|
||||
int sumOffset = scrollPosition.getCharOffset() + charOffset;
|
||||
|
||||
int lastFullChar = scrollPosition.getCharPosition() + charsPerPage;
|
||||
@@ -598,7 +602,6 @@ public class BasicCodeAreaScrolling {
|
||||
return Optional.of(targetScrollPosition);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public void updateMaximumScrollPosition(long rowsPerDocument, int rowsPerPage, int charactersPerRow, int charactersPerPage, int lastCharOffset, int lastRowOffset) {
|
||||
maximumScrollPosition.reset();
|
||||
if (rowsPerDocument > rowsPerPage) {
|
||||
@@ -629,7 +632,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
}
|
||||
|
||||
private void setHorizontalScrollPosition(CodeAreaScrollPosition scrollPosition, int charPos, int pixelOffset, int characterWidth) {
|
||||
protected void setHorizontalScrollPosition(CodeAreaScrollPosition scrollPosition, int charPos, int pixelOffset, int characterWidth) {
|
||||
switch (horizontalScrollUnit) {
|
||||
case CHARACTER: {
|
||||
scrollPosition.setCharPosition(charPos);
|
||||
@@ -720,7 +723,7 @@ public class BasicCodeAreaScrolling {
|
||||
lastVerticalScrollingValue = -1;
|
||||
}
|
||||
|
||||
private void changeVerticalExtentDifference(int newDifference) {
|
||||
protected void changeVerticalExtentDifference(int newDifference) {
|
||||
if (verticalExtentDifference != newDifference) {
|
||||
verticalExtentDifference = newDifference;
|
||||
if (verticalExtentChangeListener != null) {
|
||||
@@ -729,7 +732,7 @@ public class BasicCodeAreaScrolling {
|
||||
}
|
||||
}
|
||||
|
||||
private void changeHorizontalExtentDifference(int newDifference) {
|
||||
protected void changeHorizontalExtentDifference(int newDifference) {
|
||||
if (horizontalExtentDifference != newDifference) {
|
||||
horizontalExtentDifference = newDifference;
|
||||
if (horizontalExtentChangeListener != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,8 +19,6 @@ import org.exbin.bined.CodeAreaSection;
|
||||
|
||||
/**
|
||||
* Enumeration of basic cursor position section.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum BasicCodeAreaSection implements CodeAreaSection {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,28 +27,26 @@ import org.exbin.bined.capability.ViewModeCapable;
|
||||
|
||||
/**
|
||||
* Code area data representation structure for basic variant.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BasicCodeAreaStructure {
|
||||
|
||||
private final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
|
||||
protected final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
|
||||
@Nonnull
|
||||
private CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
|
||||
protected CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
|
||||
|
||||
@Nonnull
|
||||
private CodeType codeType = CodeType.HEXADECIMAL;
|
||||
protected CodeType codeType = CodeType.HEXADECIMAL;
|
||||
|
||||
private long dataSize;
|
||||
protected long dataSize;
|
||||
@Nonnull
|
||||
private RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
|
||||
private int maxBytesPerLine;
|
||||
private int wrappingBytesGroupSize;
|
||||
protected RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
|
||||
protected int maxBytesPerLine;
|
||||
protected int wrappingBytesGroupSize;
|
||||
|
||||
private long rowsPerDocument;
|
||||
private int bytesPerRow;
|
||||
private int charactersPerRow;
|
||||
protected long rowsPerDocument;
|
||||
protected int bytesPerRow;
|
||||
protected int charactersPerRow;
|
||||
|
||||
public BasicCodeAreaStructure() {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,8 +19,6 @@ import org.exbin.bined.CodeAreaZone;
|
||||
|
||||
/**
|
||||
* Enumeration of basic code area zones.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum BasicCodeAreaZone implements CodeAreaZone {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Code area scrolling position.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class CodeAreaScrollPosition {
|
||||
@@ -30,22 +28,22 @@ public class CodeAreaScrollPosition {
|
||||
/**
|
||||
* Scrollbar document row position.
|
||||
*/
|
||||
private long rowPosition = 0;
|
||||
protected long rowPosition = 0;
|
||||
|
||||
/**
|
||||
* Scrollbar document row pixel offset position.
|
||||
*/
|
||||
private int rowOffset = 0;
|
||||
protected int rowOffset = 0;
|
||||
|
||||
/**
|
||||
* Scrollbar document character position.
|
||||
*/
|
||||
private int charPosition = 0;
|
||||
protected int charPosition = 0;
|
||||
|
||||
/**
|
||||
* Scrollbar document character pixel offset position.
|
||||
*/
|
||||
private int charOffset = 0;
|
||||
protected int charOffset = 0;
|
||||
|
||||
public CodeAreaScrollPosition() {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of supported view modes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum CodeAreaViewMode {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Enumeration of modes for enter key handling.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public enum EnterKeyHandlingMode {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of horizontal scrolling unit sizes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum HorizontalScrollUnit {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of supported movement directions.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum MovementDirection {
|
||||
UP, DOWN, LEFT, RIGHT,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,6 @@ package org.exbin.bined.basic;
|
||||
/**
|
||||
* Enumeration of possible visibility states of caret position relative to
|
||||
* scrolling area.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum PositionScrollVisibility {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of vertical scaling modes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum ScrollBarVerticalScale {
|
||||
/**
|
||||
@@ -26,7 +24,7 @@ public enum ScrollBarVerticalScale {
|
||||
*/
|
||||
NORMAL,
|
||||
/**
|
||||
* Height is more than available range/precission of the scrollbar and must
|
||||
* Height is more than available range/precision of the scrollbar and must
|
||||
* be scaled.
|
||||
*/
|
||||
SCALED
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,24 +15,20 @@
|
||||
*/
|
||||
package org.exbin.bined.basic;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Scrolling view dimensions.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
@Immutable
|
||||
public class ScrollViewDimension {
|
||||
|
||||
private final int dataViewWidth;
|
||||
private final int dataViewHeight;
|
||||
private final int width;
|
||||
private final int height;
|
||||
protected int dataViewWidth;
|
||||
protected int dataViewHeight;
|
||||
protected int width;
|
||||
protected int height;
|
||||
|
||||
public ScrollViewDimension(int dataViewWidth, int dataViewHeight, int width, int height) {
|
||||
public ScrollViewDimension() {
|
||||
}
|
||||
|
||||
public void setDimension(int dataViewWidth, int dataViewHeight, int width, int height) {
|
||||
this.dataViewWidth = dataViewWidth;
|
||||
this.dataViewHeight = dataViewHeight;
|
||||
this.width = width;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of supported scrolling directions.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum ScrollingDirection {
|
||||
UP, DOWN, LEFT, RIGHT,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of selection selecting modes.
|
||||
*/
|
||||
public enum SelectingMode {
|
||||
/**
|
||||
* Selection is not updated.
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* Selection is updated.
|
||||
*/
|
||||
SELECTING
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,12 +17,10 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of modes for tab key handling.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum TabKeyHandlingMode {
|
||||
/**
|
||||
* Handle enter using java platform detection (default).
|
||||
* Handle tab key using java platform detection (default).
|
||||
*/
|
||||
PLATFORM_SPECIFIC,
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,6 @@ package org.exbin.bined.basic;
|
||||
|
||||
/**
|
||||
* Enumeration of vertical scrolling unit sizes.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
public enum VerticalScrollUnit {
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,6 @@ import org.exbin.bined.basic.BasicBackgroundPaintMode;
|
||||
|
||||
/**
|
||||
* Support for background paint mode capability.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface BackgroundPaintCapable {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,6 @@ import org.exbin.bined.basic.VerticalScrollUnit;
|
||||
|
||||
/**
|
||||
* Support for scrolling control.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface BasicScrollingCapable {
|
||||
@@ -60,7 +58,7 @@ public interface BasicScrollingCapable {
|
||||
void setVerticalScrollUnit(VerticalScrollUnit verticalScrollUnit);
|
||||
|
||||
/**
|
||||
* Returns horizotal scrollbar visibility mode.
|
||||
* Returns horizontal scrollbar visibility mode.
|
||||
*
|
||||
* @return scrollbar visibility mode
|
||||
*/
|
||||
@@ -68,7 +66,7 @@ public interface BasicScrollingCapable {
|
||||
ScrollBarVisibility getHorizontalScrollBarVisibility();
|
||||
|
||||
/**
|
||||
* Sets horizotal scrollbar visibility mode.
|
||||
* Sets horizontal scrollbar visibility mode.
|
||||
*
|
||||
* @param horizontalScrollBarVisibility scrollbar visibility mode
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) ExBin Project
|
||||
* Copyright (C) ExBin Project, https://exbin.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,8 +26,6 @@ import org.exbin.bined.CodeAreaCaretListener;
|
||||
|
||||
/**
|
||||
* Support for caret / cursor capability.
|
||||
*
|
||||
* @author ExBin Project (https://exbin.org)
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public interface CaretCapable {
|
||||
@@ -107,11 +105,11 @@ public interface CaretCapable {
|
||||
*
|
||||
* @param positionX x-coordinate
|
||||
* @param positionY y-coordinate
|
||||
* @param overflowMode overflow mode
|
||||
* @param overlapMode caret overlapping mode
|
||||
* @return mouse position
|
||||
*/
|
||||
@Nonnull
|
||||
CodeAreaCaretPosition mousePositionToClosestCaretPosition(int positionX, int positionY, CaretOverlapMode overflowMode);
|
||||
CodeAreaCaretPosition mousePositionToClosestCaretPosition(int positionX, int positionY, CaretOverlapMode overlapMode);
|
||||
|
||||
/**
|
||||
* Returns if cursor should be visible in other sections.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user