Efficient 2D array copy

This commit is contained in:
DBotThePony 2024-05-27 12:25:43 +07:00
parent 34ea66d7ea
commit 817a2f8459
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 341 additions and 25 deletions

View File

@ -4,7 +4,7 @@ kotlin.code.style=official
specifyKotlinAsDependency=false
projectGroup=ru.dbotthepony.kommons
projectVersion=2.17.2
projectVersion=2.18.0
guavaDepVersion=33.0.0
gsonDepVersion=2.8.9

View File

@ -44,9 +44,15 @@ abstract class Boolean2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: BitSet,
) : Boolean2DArray() {
private val mem = BitSet(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, BitSet(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.clone() as BitSet)
override fun get(column: Int, row: Int): Boolean {
if (column !in 0 until columns || row !in 0 until rows)
@ -74,12 +80,16 @@ abstract class Boolean2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.hashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Boolean2DArray" + super.toString()
}
}
private class View(private val parent: Boolean2DArray) : Boolean2DArray() {
private class View(val parent: Boolean2DArray) : Boolean2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -120,6 +130,31 @@ abstract class Boolean2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Boolean2DArray): Boolean2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Boolean2DArray = Impl(0, 0)
}

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.kommons.arrays
import java.nio.ByteBuffer
import java.util.*
abstract class Byte2DArray : Array2D() {
abstract operator fun get(column: Int, row: Int): Byte
@ -43,9 +44,15 @@ abstract class Byte2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: ByteArray,
) : Byte2DArray() {
private val mem = ByteArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, ByteArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Byte {
if (column !in 0 until columns || row !in 0 until rows)
@ -70,12 +77,16 @@ abstract class Byte2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Byte2DArray" + super.toString()
}
}
private class View(private val parent: Byte2DArray) : Byte2DArray() {
private class View(val parent: Byte2DArray) : Byte2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -116,6 +127,31 @@ abstract class Byte2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Byte2DArray): Byte2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Byte2DArray = Impl(0, 0)
}

View File

@ -48,9 +48,15 @@ abstract class Char2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: CharArray,
) : Char2DArray() {
private val mem = CharArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, CharArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Char {
if (column !in 0 until columns || row !in 0 until rows)
@ -75,12 +81,16 @@ abstract class Char2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Char2DArray" + super.toString()
}
}
private class View(private val parent: Char2DArray) : Char2DArray() {
private class View(val parent: Char2DArray) : Char2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -121,6 +131,31 @@ abstract class Char2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Char2DArray): Char2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Char2DArray = Impl(0, 0)
}

View File

@ -95,9 +95,15 @@ abstract class Double2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: DoubleArray
) : Double2DArray() {
private val mem = DoubleArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, DoubleArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Double {
if (column !in 0 until columns || row !in 0 until rows)
@ -122,12 +128,16 @@ abstract class Double2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Double2DArray" + super.toString()
}
}
private class View(private val parent: Double2DArray) : Double2DArray() {
private class View(val parent: Double2DArray) : Double2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -168,6 +178,31 @@ abstract class Double2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Double2DArray): Double2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Double2DArray = Impl(0, 0)
}

View File

@ -96,9 +96,15 @@ abstract class Float2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: FloatArray
) : Float2DArray() {
private val mem = FloatArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, FloatArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Float {
if (column !in 0 until columns || row !in 0 until rows)
@ -123,12 +129,16 @@ abstract class Float2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Float2DArray" + super.toString()
}
}
private class View(private val parent: Float2DArray) : Float2DArray() {
private class View(val parent: Float2DArray) : Float2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -169,6 +179,31 @@ abstract class Float2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Float2DArray): Float2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Float2DArray = Impl(0, 0)
}

View File

@ -95,9 +95,15 @@ abstract class Int2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: IntArray
) : Int2DArray() {
private val mem = IntArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, IntArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Int {
if (column !in 0 until columns || row !in 0 until rows)
@ -122,12 +128,16 @@ abstract class Int2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Int2DArray" + super.toString()
}
}
private class View(private val parent: Int2DArray) : Int2DArray() {
private class View(val parent: Int2DArray) : Int2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -168,6 +178,31 @@ abstract class Int2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Int2DArray): Int2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Int2DArray = Impl(0, 0)
}

View File

@ -95,9 +95,15 @@ abstract class Long2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: LongArray
) : Long2DArray() {
private val mem = LongArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, LongArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Long {
if (column !in 0 until columns || row !in 0 until rows)
@ -122,12 +128,16 @@ abstract class Long2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Long2DArray" + super.toString()
}
}
private class View(private val parent: Long2DArray) : Long2DArray() {
private class View(val parent: Long2DArray) : Long2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
@ -168,6 +178,31 @@ abstract class Long2DArray : Array2D() {
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Long2DArray): Long2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Long2DArray = Impl(0, 0)
}

View File

@ -46,7 +46,7 @@ class Object2DArray<T> private constructor(
constructor(
parent: Object2DArray<T>
) : this(parent.columns, parent.rows, Array(parent.size) { parent.mem[it] })
) : this(parent.columns, parent.rows, parent.mem.copyOf())
init {
require(columns >= 0 && rows >= 0) { "Invalid dimensions: $columns x $rows" }

View File

@ -48,9 +48,15 @@ abstract class Short2DArray : Array2D() {
private class Impl(
override val columns: Int,
override val rows: Int
override val rows: Int,
private val mem: ShortArray
) : Short2DArray() {
private val mem = ShortArray(columns * rows)
constructor(
columns: Int,
rows: Int,
) : this(columns, rows, ShortArray(columns * rows))
constructor(other: Impl) : this(other.columns, other.rows, other.mem.copyOf())
override fun get(column: Int, row: Int): Short {
if (column !in 0 until columns || row !in 0 until rows)
@ -75,17 +81,81 @@ abstract class Short2DArray : Array2D() {
return super.equals(other)
}
override fun hashCode(): Int {
return mem.contentHashCode().rotateLeft(13) + columns * 31 + rows * 31 * 31
}
override fun toString(): String {
return "Short2DArray" + super.toString()
}
}
private class View(val parent: Short2DArray) : Short2DArray() {
override val rows: Int
get() = parent.rows
override val columns: Int
get() = parent.columns
override fun get(column: Int, row: Int): Short {
return parent[column, row]
}
override fun set(column: Int, row: Int, value: Short) {
throw UnsupportedOperationException("Read only view")
}
override fun equals(other: Any?): Boolean {
return other is View && parent == other.parent
}
override fun hashCode(): Int {
return parent.hashCode()
}
override fun toString(): String {
return "View = $parent"
}
}
companion object {
@JvmStatic
fun allocate(columns: Int, rows: Int): Short2DArray {
return Impl(columns, rows)
}
@JvmStatic
fun unmodifiable(parent: Short2DArray): Short2DArray {
if (parent is View)
return parent
else
return View(parent)
}
/**
* Copies specified 2D array.
*
* This method may be more efficient than doing it manually,
* and semantically is equivalent to next code:
*
* ```kotlin
* val result = allocate(value.columns, value.rows)
* result.load(value)
* return result
* ```
*/
@JvmStatic
fun copy(value: Short2DArray): Short2DArray {
if (value is View) {
return copy(value.parent)
} else if (value is Impl) {
return Impl(value)
} else {
val result = Impl(value.columns, value.rows)
result.load(value)
return result
}
}
@JvmField
val ZERO: Short2DArray = Impl(0, 0)
}