Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perf/misc ds improvements #692

Merged
merged 36 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d58770b
feature: add IndexedStringBuilder
lanarimarco Dec 29, 2024
a1c1cd4
feature: add IndexedStringBuilderTest
lanarimarco Dec 29, 2024
797dedc
improve replace function
lanarimarco Dec 29, 2024
ec83408
improve replace function that now works only with the chunks implied
lanarimarco Dec 29, 2024
422c4b0
refactor: move IndexedStringBuilder comparison logic
lanarimarco Jan 1, 2025
3e9bcde
typo
lanarimarco Jan 1, 2025
f73d4f3
refactor: pass chunksSize to compareStringBuilderAndIndexedStringBuil…
lanarimarco Jan 1, 2025
24e7004
refactor: move IndexedStringBuilder to datastruct_value_utils module
lanarimarco Jan 1, 2025
189e952
feat: add NotIndexedStringBuilder and DataStructStringValue.create fu…
lanarimarco Jan 1, 2025
e304e69
perf: improve substring by searching into subChunks
lanarimarco Jan 1, 2025
65f3635
refactor: change function name a pass to stringBuilderVsDataStructStr…
lanarimarco Jan 1, 2025
544ac00
format
lanarimarco Jan 1, 2025
988a010
typo
lanarimarco Jan 1, 2025
4ed74a9
refactor: change algorithm for choice of StringBuilder rather than In…
lanarimarco Jan 1, 2025
2ca4e8e
chore: createPerformanceComparisonDataset compare the ratio both sb/i…
lanarimarco Jan 1, 2025
1973e56
fix: IndexedStringBuilder replace does not work when replacingString …
lanarimarco Jan 1, 2025
7a646d5
refactor and add doc
lanarimarco Jan 1, 2025
20b7600
typo
lanarimarco Jan 1, 2025
e2563be
test: add DataStructValueBuilderTest
lanarimarco Jan 1, 2025
1f222b6
refactor
lanarimarco Jan 1, 2025
7983637
feature: replace DataStructValue.value type from StringBuilder to Dat…
lanarimarco Jan 4, 2025
5cd4fae
feature: add missing methods to DataStructValueBuilder to make it com…
lanarimarco Jan 4, 2025
3110a83
feature: add all stuff related DataStructValueBuilder serialization
lanarimarco Jan 5, 2025
daf1a0e
refactor: change name of test suite
lanarimarco Jan 5, 2025
38219e0
fix: substring where chunk containing string to replace is not at the…
lanarimarco Jan 5, 2025
be765e2
feature: add to DataStruct value new blank function
lanarimarco Jan 5, 2025
dec4dc3
feature: add or update initializer functions for DataStructValue to h…
lanarimarco Jan 5, 2025
127916e
refactor: remove the dataStructStringValueDuration
lanarimarco Jan 7, 2025
227ce49
refactor: `DataStructValueBuilder.create` by adding context more help…
lanarimarco Jan 7, 2025
e73a31d
perf: in decodeFromDS replaced String with StringBuilder whenever pos…
lanarimarco Jan 10, 2025
4b53be0
perf: in decodeFromDS all logic related the conversion when the strin…
lanarimarco Jan 11, 2025
d15cff9
test: Add DataDefinitionPerformanceTest tests suite
lanarimarco Jan 11, 2025
fbdbd8c
Revert "perf: in decodeFromDS all logic related the conversion when t…
lanarimarco Jan 12, 2025
e474f9f
perf: in decodeFromDS all logic related the conversion when the strin…
lanarimarco Jan 12, 2025
e535a86
chore: fix warnings
lanarimarco Jan 12, 2025
43d2d38
Merge branch 'develop' into perf/misc_ds_improvements
lanarimarco Jan 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,23 @@ data class JarikoCallback(
*/
var finishRpgTrace: (() -> Unit) = {
// Defaults to a no-op
},

/**
* Creates a `DataStructValueBuilder` based on the provided value and data structure type.
* The default implementation creates an `IndexedStringBuilder` for data structures with 100 or more fields
* and all fields are arrays, elsewhere it creates a `StringBuilderWrapper`.
* @param value The string value to be used for the data structure.
* @param type The data structure type which defines the fields.
* @return A `DataStructValueBuilder` instance.
*/
var createDataStructValueBuilder: ((value: String, type: DataStructureType) -> DataStructValueBuilder) = { value, type ->
val totalFields = type.totalFields()
if (totalFields >= 100 && type.containsOnlyArrays()) {
IndexedStringBuilder(value = value, chunksSize = value.length / totalFields)
} else {
StringBuilderWrapper(value)
}
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ fun Type.lowValue(): Value {
is ArrayType -> createArrayValue(this.element, this.nElements) { coerce(LowValValue, this.element) }
is DataStructureType -> {
val fields = this.fields.associateWith { field -> field.type.lowValue() }
DataStructValue.fromFields(fields)
DataStructValue.fromFields(fields = fields, type = this)
}
is BooleanType -> BooleanValue.FALSE
is RecordFormatType -> BlanksValue
Expand All @@ -323,7 +323,7 @@ fun Type.hiValue(): Value {
is ArrayType -> createArrayValue(this.element, this.nElements) { coerce(HiValValue, this.element) }
is DataStructureType -> {
val fields = this.fields.associateWith { field -> field.type.hiValue() }
DataStructValue.fromFields(fields)
DataStructValue.fromFields(fields = fields, type = this)
}
is BooleanType -> BooleanValue.TRUE
is RecordFormatType -> BlanksValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import java.math.BigDecimal
import java.util.*

@Serializable
abstract class AbstractDataDefinition(
Expand Down Expand Up @@ -166,7 +167,7 @@ data class FileDefinition private constructor(

var internalFormatName: String? = null
set(value) {
field = value?.toUpperCase()
field = value?.uppercase(Locale.getDefault())
}

private var fieldNameToDataDefinitionName = mutableMapOf<String, String>()
Expand Down Expand Up @@ -505,15 +506,15 @@ fun encodeBinary(inValue: BigDecimal, size: Int): String {

buffer[0] = (lsb and 0x0000FFFF).toByte()

return buffer[0].toChar().toString()
return buffer[0].toInt().toChar().toString()
}

if (size == 2) {

buffer[0] = ((lsb shr 8) and 0x000000FF).toByte()
buffer[1] = (lsb and 0x000000FF).toByte()

return buffer[1].toChar().toString() + buffer[0].toChar().toString()
return buffer[1].toInt().toChar().toString() + buffer[0].toInt().toChar().toString()
}
if (size == 4) {

Expand All @@ -522,7 +523,8 @@ fun encodeBinary(inValue: BigDecimal, size: Int): String {
buffer[2] = ((lsb shr 8) and 0x0000FFFF).toByte()
buffer[3] = (lsb and 0x0000FFFF).toByte()

return buffer[3].toChar().toString() + buffer[2].toChar().toString() + buffer[1].toChar().toString() + buffer[0].toChar().toString()
return buffer[3].toInt().toChar().toString() + buffer[2].toInt().toChar().toString() + buffer[1].toInt()
.toChar().toString() + buffer[0].toInt().toChar().toString()
}
if (size == 8) {
val llsb = inValue.toLong()
Expand All @@ -535,8 +537,10 @@ fun encodeBinary(inValue: BigDecimal, size: Int): String {
buffer[6] = ((llsb shr 8) and 0x0000FFFF).toByte()
buffer[7] = (llsb and 0x0000FFFF).toByte()

return buffer[7].toChar().toString() + buffer[6].toChar().toString() + buffer[5].toChar().toString() + buffer[4].toChar().toString() +
buffer[3].toChar().toString() + buffer[2].toChar().toString() + buffer[1].toChar().toString() + buffer[0].toChar().toString()
return buffer[7].toInt().toChar().toString() + buffer[6].toInt().toChar().toString() + buffer[5].toInt()
.toChar().toString() + buffer[4].toInt().toChar().toString() +
buffer[3].toInt().toChar().toString() + buffer[2].toInt().toChar().toString() + buffer[1].toInt().toChar().toString() + buffer[0].toInt()
.toChar().toString()
}
TODO("encode binary for $size not implemented")
}
Expand All @@ -552,39 +556,39 @@ fun encodeUnsigned(inValue: BigDecimal, size: Int): String {
fun decodeBinary(value: String, size: Int): BigDecimal {
if (size == 1) {
var number: Long = 0x0000000
if (value[0].toInt() and 0x0010 != 0) {
if (value[0].code and 0x0010 != 0) {
number = 0x00000000
}
number += (value[0].toInt() and 0x00FF)
number += (value[0].code and 0x00FF)
return BigDecimal(number.toInt().toString())
}

if (size == 2) {
var number: Long = 0x0000000
if (value[1].toInt() and 0x8000 != 0) {
if (value[1].code and 0x8000 != 0) {
number = 0xFFFF0000
}
number += (value[0].toInt() and 0x00FF) + ((value[1].toInt() and 0x00FF) shl 8)
number += (value[0].code and 0x00FF) + ((value[1].code and 0x00FF) shl 8)
return BigDecimal(number.toInt().toString())
}

if (size == 4) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24)

return BigDecimal(number.toInt().toString())
}
if (size == 8) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24) +
((value[4].toLong() and 0x00FF) shl 32) +
((value[5].toLong() and 0x00FF) shl 40) +
((value[6].toLong() and 0x00FF) shl 48) +
((value[7].toLong() and 0x00FF) shl 56)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24) +
((value[4].code.toLong() and 0x00FF) shl 32) +
((value[5].code.toLong() and 0x00FF) shl 40) +
((value[6].code.toLong() and 0x00FF) shl 48) +
((value[7].code.toLong() and 0x00FF) shl 56)

return BigDecimal(number.toInt().toString())
}
Expand All @@ -594,35 +598,35 @@ fun decodeBinary(value: String, size: Int): BigDecimal {
fun decodeInteger(value: String, size: Int): BigDecimal {
if (size == 1) {
var number = 0x0000000
number += (value[0].toByte())
number += (value[0].code.toByte())
return BigDecimal(number.toString())
}

if (size == 2) {
var number: Long = 0x0000000
if (value[1].toInt() and 0x8000 != 0) {
if (value[1].code and 0x8000 != 0) {
number = 0xFFFF0000
}
number += (value[0].toInt() and 0x00FF) + ((value[1].toInt() and 0x00FF) shl 8)
number += (value[0].code and 0x00FF) + ((value[1].code and 0x00FF) shl 8)
return BigDecimal(number.toInt().toString())
}
if (size == 4) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24)

return BigDecimal(number.toInt().toString())
}
if (size == 8) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24) +
((value[4].toLong() and 0x00FF) shl 32) +
((value[5].toLong() and 0x00FF) shl 40) +
((value[6].toLong() and 0x00FF) shl 48) +
((value[7].toLong() and 0x00FF) shl 56)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24) +
((value[4].code.toLong() and 0x00FF) shl 32) +
((value[5].code.toLong() and 0x00FF) shl 40) +
((value[6].code.toLong() and 0x00FF) shl 48) +
((value[7].code.toLong() and 0x00FF) shl 56)

return BigDecimal(number.toString())
}
Expand All @@ -633,40 +637,40 @@ fun decodeUnsigned(value: String, size: Int): BigDecimal {

if (size == 1) {
var number: Long = 0x0000000
if (value[0].toInt() and 0x0010 != 0) {
if (value[0].code and 0x0010 != 0) {
number = 0x00000000
}
number += (value[0].toInt() and 0x00FF)
number += (value[0].code and 0x00FF)
return BigDecimal(number.toInt().toString())
}

if (size == 2) {
var number: Long = 0x0000000
if (value[1].toInt() and 0x1000 != 0) {
if (value[1].code and 0x1000 != 0) {
number = 0xFFFF0000
}
number += (value[0].toInt() and 0x00FF) + ((value[1].toInt() and 0x00FF) shl 8)
number += (value[0].code and 0x00FF) + ((value[1].code and 0x00FF) shl 8)
// make sure you count onlu 16 bits
number = number and 0x0000FFFF
return BigDecimal(number.toString())
}
if (size == 4) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24)

return BigDecimal(number.toString())
}
if (size == 8) {
val number = (value[0].toLong() and 0x00FF) +
((value[1].toLong() and 0x00FF) shl 8) +
((value[2].toLong() and 0x00FF) shl 16) +
((value[3].toLong() and 0x00FF) shl 24) +
((value[4].toLong() and 0x00FF) shl 32) +
((value[5].toLong() and 0x00FF) shl 40) +
((value[6].toLong() and 0x00FF) shl 48) +
((value[7].toLong() and 0x00FF) shl 56)
val number = (value[0].code.toLong() and 0x00FF) +
((value[1].code.toLong() and 0x00FF) shl 8) +
((value[2].code.toLong() and 0x00FF) shl 16) +
((value[3].code.toLong() and 0x00FF) shl 24) +
((value[4].code.toLong() and 0x00FF) shl 32) +
((value[5].code.toLong() and 0x00FF) shl 40) +
((value[6].code.toLong() and 0x00FF) shl 48) +
((value[7].code.toLong() and 0x00FF) shl 56)

return BigDecimal(number.toInt().toString())
}
Expand All @@ -685,7 +689,7 @@ fun encodeToZoned(inValue: BigDecimal, digits: Int, scale: Int): String {
val sign = inValue.signum()

inChars.forEachIndexed { index, char ->
val digit = char.toInt()
val digit = char.code
buffer[index] = digit
}
if (sign < 0) {
Expand All @@ -708,11 +712,11 @@ fun decodeFromZoned(value: String, digits: Int, scale: Int): BigDecimal {
when {
it.isDigit() -> builder.append(it)
else -> {
if (it.toInt() == 0) {
if (it.code == 0) {
builder.append('0')
} else {
builder.insert(0, '-')
builder.append((it.toInt() - 0x0049 + 0x0030).toChar())
builder.append((it.code - 0x0049 + 0x0030).toChar())
}
}
}
Expand Down Expand Up @@ -741,16 +745,16 @@ fun encodeToDS(inValue: BigDecimal, digits: Int, scale: Int): String {

// place all the digits except last one
while (inPosition < inChars.size - 1) {
firstNibble = ((inChars[inPosition++].toInt()) and 0x000F) shl 4
secondNibble = (inChars[inPosition++].toInt()) and 0x000F
firstNibble = ((inChars[inPosition++].code) and 0x000F) shl 4
secondNibble = (inChars[inPosition++].code) and 0x000F
buffer[offset++] = (firstNibble + secondNibble)
}

// place last digit and sign nibble
firstNibble = if (inPosition == inChars.size) {
0x00F0
} else {
(inChars[inChars.size - 1].toInt()) and 0x000F shl 4
(inChars[inChars.size - 1].code) and 0x000F shl 4
}
if (sign != -1) {
buffer[offset] = (firstNibble + 0x000F)
Expand All @@ -767,47 +771,43 @@ fun encodeToDS(inValue: BigDecimal, digits: Int, scale: Int): String {
}

fun decodeFromDS(value: String, digits: Int, scale: Int): BigDecimal {
val buffer = IntArray(value.length)
for (i in value.indices) {
buffer[i] = value[i].toInt()
}
return try {
value.toBigDecimal()
} catch (e: Exception) {
val buffer = IntArray(value.length)
for (i in value.indices) {
buffer[i] = value[i].code
}

var sign = ""
var number = ""
var nibble = ((buffer[buffer.size - 1]) and 0x0F)
if (nibble == 0x0B || nibble == 0x0D) {
sign = "-"
}
val sign = StringBuilder()
val number = StringBuilder()
var nibble = (buffer[buffer.size - 1] and 0x0F)
if (nibble == 0x0B || nibble == 0x0D) {
sign.append("-")
}

var offset = 0
while (offset < (buffer.size - 1)) {
nibble = (buffer[offset] and 0xFF).ushr(4)
number += Character.toString((nibble or 0x30).toChar())
nibble = buffer[offset] and 0x0F or 0x30
number += Character.toString((nibble or 0x30).toChar())
var offset = 0
while (offset < (buffer.size - 1)) {
nibble = (buffer[offset] and 0xFF).ushr(4)
number.append((nibble or 0x30).toChar())
nibble = buffer[offset] and 0x0F or 0x30
number.append((nibble or 0x30).toChar())

offset++
}
offset++
}

// read last digit
nibble = (buffer[offset] and 0xFF).ushr(4)
if (nibble <= 9) {
number += Character.toString((nibble or 0x30).toChar())
}
// adjust the scale
if (scale > 0 && number != "0") {
val len = number.length
number = buildString {
append(number.substring(0, len - scale))
append(".")
append(number.substring(len - scale, len))
// read last digit
nibble = (buffer[offset] and 0xFF).ushr(4)
if (nibble <= 9) {
number.append((nibble or 0x30).toChar())
}
}
number = sign + number
return try {
value.toBigDecimal()
} catch (e: Exception) {
number.toBigDecimal()
// adjust the scale
if (scale > 0 && number.toString() != "0") {
val len = number.length
number.insert(len - scale, ".")
}
number.insert(0, sign)
number.toString().toBigDecimal()
}
}

Expand Down
Loading
Loading