Skip to content

Commit 8787b83

Browse files
Merge pull request #16 from runetopic/development
Cache loading improvements.
2 parents 66b25e4 + 7e97975 commit 8787b83

File tree

10 files changed

+98
-79
lines changed

10 files changed

+98
-79
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ A cache library written in Kotlin.
2929
# Implementation
3030
Just use cache if you do not require any of the revision specific loaders.
3131
```
32-
cache = { module = "com.runetopic.cache:cache", version.ref "1.4.8-SNAPSHOT" }
32+
cache = { module = "com.runetopic.cache:cache", version.ref "1.4.10-SNAPSHOT" }
3333
loader = { module = "com.runetopic.cache:loader", version.ref "647.6.1-SNAPSHOT" }
3434
```
3535

cache/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
signing
55
}
66

7-
version = "1.4.8-SNAPSHOT"
7+
version = "1.4.10-SNAPSHOT"
88

99
java {
1010
withJavadocJar()
@@ -77,5 +77,5 @@ dependencies {
7777
implementation("com.michael-bull.kotlin-inline-logger:kotlin-inline-logger:1.0.3")
7878
implementation("org.slf4j:slf4j-simple:1.7.32")
7979
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.+")
80-
implementation("com.runetopic.cryptography:cryptography:1.0.2-SNAPSHOT")
80+
implementation("com.runetopic.cryptography:cryptography:1.0.4-SNAPSHOT")
8181
}

cache/src/main/kotlin/com/runetopic/cache/codec/ContainerCodec.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.runetopic.cache.codec.CodecType.*
44
import com.runetopic.cache.exception.CompressionException
55
import com.runetopic.cache.extension.readUnsignedShort
66
import com.runetopic.cache.extension.remainingBytes
7-
import com.runetopic.cryptography.ext.fromXTEA
7+
import com.runetopic.cryptography.fromXTEA
88
import java.nio.ByteBuffer
99
import java.util.zip.CRC32
1010

cache/src/main/kotlin/com/runetopic/cache/extension/ByteBuffer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.runetopic.cache.extension
22

33
import com.runetopic.cache.store.Constants.cp1252Identifiers
4-
import com.runetopic.cryptography.ext.toWhirlpool
4+
import com.runetopic.cryptography.toWhirlpool
55
import java.nio.ByteBuffer
66
import java.nio.charset.Charset
77

cache/src/main/kotlin/com/runetopic/cache/hierarchy/ReferenceTable.kt

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.runetopic.cache.hierarchy
22

3-
import com.runetopic.cache.codec.ContainerCodec
3+
import com.runetopic.cache.codec.Container
44
import com.runetopic.cache.codec.decompress
55
import com.runetopic.cache.exception.ProtocolException
66
import com.runetopic.cache.extension.readUnsignedByte
@@ -59,31 +59,49 @@ internal data class ReferenceTable(
5959

6060
fun exists(): Boolean = (length != 0 && sector != 0)
6161

62-
fun loadIndex(datFile: IDatFile, idxFile: IIdxFile, whirlpool: ByteArray, data: ByteArray): Js5Index {
63-
val container = ContainerCodec.decompress(data)
64-
val buffer = ByteBuffer.wrap(container.data)
62+
fun loadIndex(
63+
datFile: IDatFile,
64+
idxFile: IIdxFile,
65+
whirlpool: ByteArray,
66+
decompressed: Container
67+
): Js5Index {
68+
val buffer = ByteBuffer.wrap(decompressed.data)
69+
val crc = decompressed.crc
70+
val compressionType = decompressed.compression
6571
val protocol = buffer.readUnsignedByte()
66-
var revision = 0
67-
68-
if (protocol < 5 || protocol > 6) {
69-
throw ProtocolException("Unhandled protocol $protocol when reading index $this")
72+
val revision = when {
73+
protocol < 5 || protocol > 6 -> throw ProtocolException("Unhandled protocol $protocol when reading index $this")
74+
protocol == 6 -> buffer.int
75+
else -> 0
7076
}
71-
72-
if (protocol >= 6) {
73-
revision = buffer.int
77+
val hash = buffer.readUnsignedByte()
78+
val count = buffer.readUnsignedShort()
79+
val groupTables = mutableListOf<ByteArray>()
80+
(0 until count).forEach {
81+
groupTables.add(datFile.readReferenceTable(idxFile.id(), idxFile.loadReferenceTable(it)))
7482
}
83+
return loadIndexContents(idxFile.id(), buffer, crc, compressionType, revision, protocol, hash, count, whirlpool, groupTables)
84+
}
85+
86+
private fun loadIndexContents(
87+
indexId: Int,
88+
buffer: ByteBuffer,
89+
crc: Int,
90+
compressionType: Int,
91+
revision: Int,
92+
protocol: Int,
93+
hash: Int,
94+
count: Int,
95+
whirlpool: ByteArray,
96+
groupTables: List<ByteArray>
97+
): Js5Index {
98+
val groupIds = IntArray(count)
7599

76-
val hash = buffer.readUnsignedByte()
77100
val isNamed = (0x1 and hash) != 0
78101
val isUsingWhirlPool = (0x2 and hash) != 0
79102

80-
val count = buffer.readUnsignedShort()
81-
82103
var lastGroupId = 0
83104
var biggest = -1
84-
85-
val groupIds = IntArray(count)
86-
87105
(0 until count).forEach {
88106
groupIds[it] = buffer.readUnsignedShort().let { id -> lastGroupId += id; lastGroupId }
89107
if (groupIds[it] > biggest) biggest = groupIds[it]
@@ -101,19 +119,18 @@ internal data class ReferenceTable(
101119
val groups = hashMapOf<Int, Js5Group>()
102120
(0 until count).forEach {
103121
val groupId = groupIds[it]
104-
val groupReferenceTable = datFile.readReferenceTable(idxFile.id(), idxFile.loadReferenceTable(it))
105122
groups[it] = (Js5Group(
106123
id = groupId,
107124
nameHash = groupNameHashes[groupId],
108125
crc = groupCrcs[groupId],
109126
whirlpool = groupWhirlpools[groupId],
110127
revision = groupRevisions[groupId],
111128
keys = intArrayOf(),
112-
files = files(fileIds, fileNameHashes, groupReferenceTable, groupFileIds[it], it),
113-
data = groupReferenceTable
129+
files = files(fileIds, fileNameHashes, groupTables[it], groupFileIds[it], it),
130+
data = groupTables[it]
114131
))
115132
}
116-
return Js5Index(idxFile.id(), container.crc, whirlpool, container.compression, protocol, revision, isNamed, groups)
133+
return Js5Index(indexId, crc, whirlpool, compressionType, protocol, revision, isNamed, groups)
117134
}
118135

119136
private fun groupFileIds(
@@ -195,8 +212,8 @@ internal data class ReferenceTable(
195212
count: Int,
196213
groupIds: IntArray,
197214
buffer: ByteBuffer
198-
): Array<Array<Int>> {
199-
val fileIds = Array(largestGroupId) { Array(validFileIds[it]) { -1 } }
215+
): Array<IntArray> {
216+
val fileIds = Array(largestGroupId) { IntArray(validFileIds[it]) }
200217
(0 until count).forEach {
201218
val groupId = groupIds[it]
202219
var currentFileId = 0
@@ -216,8 +233,8 @@ internal data class ReferenceTable(
216233
groupIds: IntArray,
217234
buffer: ByteBuffer,
218235
isNamed: Boolean
219-
): Array<Array<Int>> {
220-
val fileNameHashes = Array(largestGroupId) { Array(validFileIds[it]) { -1 } }
236+
): Array<IntArray> {
237+
val fileNameHashes = Array(largestGroupId) { IntArray(validFileIds[it]) }
221238
if (isNamed) {
222239
(0 until count).forEach {
223240
val groupId = groupIds[it]
@@ -230,8 +247,8 @@ internal data class ReferenceTable(
230247
}
231248

232249
private fun files(
233-
fileIds: Array<Array<Int>>,
234-
fileNameHashes: Array<Array<Int>>,
250+
fileIds: Array<IntArray>,
251+
fileNameHashes: Array<IntArray>,
235252
groupReferenceTableData: ByteArray,
236253
count: Int,
237254
groupId: Int
@@ -249,7 +266,7 @@ internal data class ReferenceTable(
249266
}
250267

251268
var position = src.size
252-
val chunks: Int = src[--position].toInt() and 0xFF
269+
val chunks = src[--position].toInt() and 0xFF
253270
position -= chunks * (count * 4)
254271
val buffer = ByteBuffer.wrap(src)
255272
buffer.position(position)

cache/src/main/kotlin/com/runetopic/cache/store/Js5Store.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.runetopic.cache.store
22

33
import com.runetopic.cache.hierarchy.index.Index
44
import com.runetopic.cache.store.storage.js5.Js5DiskStorage
5-
import com.runetopic.cryptography.ext.toWhirlpool
5+
import com.runetopic.cryptography.toWhirlpool
66
import java.io.Closeable
77
import java.math.BigInteger
88
import java.nio.ByteBuffer
@@ -21,15 +21,17 @@ class Js5Store(
2121
private val indexes = arrayListOf<Index>()
2222

2323
init {
24-
this.storage.init(this)
24+
storage.init(this)
25+
indexes.sortWith(compareBy { it.getId() })
2526
}
2627

28+
@Synchronized
2729
internal fun addIndex(index: Index) {
2830
indexes.forEach { i -> require(index.getId() != i.getId()) { "Index with Id={${index.getId()}} already exists." } }
29-
this.indexes.add(index)
31+
indexes.add(index)
3032
}
3133

32-
fun index(indexId: Int): Index = this.indexes.find { it.getId() == indexId }!!
34+
fun index(indexId: Int): Index = indexes.find { it.getId() == indexId }!!
3335

3436
fun indexReferenceTableSize(indexId: Int): Int {
3537
var size = 0
@@ -77,12 +79,12 @@ class Js5Store(
7779

7880
val whirlpool = ByteBuffer.allocate(64 + 1)
7981
whirlpool.put(1)
80-
whirlpool.put(ByteBuffer.wrap(headerArray, 5, headerPosition - 5).array().toWhirlpool())
81-
val rsa = BigInteger(whirlpool.array()).modPow(exponent, modulus).toByteArray()
82+
whirlpool.put(headerArray.copyInto(ByteArray(headerPosition - 5), 0, 5, headerPosition).toWhirlpool())
8283

83-
val checksums = ByteBuffer.allocate(headerPosition.plus(rsa.size))
84+
val rsa = BigInteger(whirlpool.array()).modPow(exponent, modulus).toByteArray()
85+
val checksums = ByteBuffer.allocate(headerPosition + rsa.size)
8486
checksums.put(0)
85-
checksums.putInt((headerPosition.plus(rsa.size)) - 5)
87+
checksums.putInt((headerPosition + rsa.size) - 5)
8688
checksums.put(headerArray, 5, headerPosition - 5)
8789
checksums.put(rsa)
8890
return checksums.array()

cache/src/main/kotlin/com/runetopic/cache/store/storage/IStorage.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package com.runetopic.cache.store.storage
22

3-
import com.runetopic.cache.hierarchy.ReferenceTable
43
import com.runetopic.cache.hierarchy.index.Index
54
import com.runetopic.cache.store.Js5Store
65
import java.io.Closeable
76
import java.io.Flushable
7+
import java.util.concurrent.CountDownLatch
88

99
/**
1010
* @author Tyler Telis
1111
* @email <[email protected]>
1212
*/
1313
internal interface IStorage: Closeable, Flushable {
1414
fun init(store: Js5Store)
15-
fun loadIndex(table: ReferenceTable, indexId: Int, whirlpool: ByteArray, referenceTable: ByteArray): Index
15+
fun store(indexId: Int, store: Js5Store, latch: CountDownLatch)
1616
fun loadReferenceTable(index: Index, groupId: Int): ByteArray
1717
fun loadMasterReferenceTable(groupId: Int): ByteArray
1818
fun loadReferenceTable(index: Index, groupName: String): ByteArray

cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5DiskStorage.kt

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
package com.runetopic.cache.store.storage.js5
22

33
import com.github.michaelbull.logging.InlineLogger
4+
import com.runetopic.cache.codec.ContainerCodec
45
import com.runetopic.cache.extension.whirlpool
5-
import com.runetopic.cache.hierarchy.ReferenceTable
66
import com.runetopic.cache.hierarchy.index.Index
77
import com.runetopic.cache.hierarchy.index.Js5Index
88
import com.runetopic.cache.store.Constants
99
import com.runetopic.cache.store.Js5Store
1010
import com.runetopic.cache.store.storage.IStorage
1111
import com.runetopic.cache.store.storage.js5.impl.DatFile
1212
import com.runetopic.cache.store.storage.js5.impl.IdxFile
13-
import java.io.File
1413
import java.io.FileNotFoundException
1514
import java.nio.ByteBuffer
1615
import java.nio.file.Path
1716
import java.util.concurrent.CountDownLatch
1817
import java.util.concurrent.ExecutorService
1918
import java.util.concurrent.Executors
2019
import java.util.concurrent.TimeUnit
20+
import kotlin.io.path.ExperimentalPathApi
21+
import kotlin.io.path.exists
2122

2223
/**
2324
* @author Tyler Telis
2425
* @email <[email protected]>
2526
*/
27+
@OptIn(ExperimentalPathApi::class)
2628
internal class Js5DiskStorage(
2729
private val path: Path
2830
) : IStorage {
@@ -32,13 +34,13 @@ internal class Js5DiskStorage(
3234
private val logger = InlineLogger()
3335

3436
init {
35-
val masterIndexFile = File("${path}/${Constants.MAIN_FILE_255}")
37+
val masterIndexFile = Path.of("${path}/${Constants.MAIN_FILE_255}")
3638

3739
if (masterIndexFile.exists().not()) {
3840
throw FileNotFoundException("Missing ${Constants.MAIN_FILE_255} in directory ${path}/${Constants.MAIN_FILE_255}")
3941
}
4042

41-
val datFile = File("${path}/${Constants.MAIN_FILE_DAT}")
43+
val datFile = Path.of("${path}/${Constants.MAIN_FILE_DAT}")
4244

4345
if (datFile.exists().not()) {
4446
throw FileNotFoundException("Missing ${Constants.MAIN_FILE_DAT} in directory ${path}/${Constants.MAIN_FILE_DAT}")
@@ -56,34 +58,29 @@ internal class Js5DiskStorage(
5658
}
5759
val latch = CountDownLatch(masterIdxFile.validIndexCount())
5860

59-
(0 until masterIdxFile.validIndexCount()).forEach {
60-
val referenceTable = masterIdxFile.loadReferenceTable(it)
61-
idxFiles.add(getIdxFile(it))
62-
63-
if (referenceTable.exists().not()) {
64-
store.addIndex(Js5Index.default(it))
65-
latch.countDown()
66-
return@forEach
67-
}
68-
val datTable = datFile.readReferenceTable(masterIdxFile.id(), referenceTable)
69-
70-
pool?.let { service ->
71-
service.execute {
72-
store.addIndex(loadIndex(referenceTable, it, ByteBuffer.wrap(datTable).whirlpool(), datTable))
73-
latch.countDown()
74-
}
75-
} ?: run {
76-
store.addIndex(loadIndex(referenceTable, it, ByteBuffer.wrap(datTable).whirlpool(), datTable))
77-
latch.countDown()
78-
}
61+
(0 until masterIdxFile.validIndexCount()).forEach { indexId ->
62+
pool?.let { it.execute { store(indexId, store, latch) } }
63+
?: run { store(indexId, store, latch) }
7964
}
65+
8066
latch.await(60, TimeUnit.SECONDS)
8167
pool?.shutdown()
82-
logger.debug { "Loaded ${idxFiles.size} indices." }
68+
logger.debug { "Loaded ${idxFiles.size} indexes." }
8369
}
8470

85-
override fun loadIndex(table: ReferenceTable, indexId: Int, whirlpool: ByteArray, referenceTable: ByteArray): Js5Index {
86-
return table.loadIndex(datFile, getIdxFile(indexId), whirlpool, referenceTable)
71+
override fun store(indexId: Int, store: Js5Store, latch: CountDownLatch) {
72+
val indexTable = masterIdxFile.loadReferenceTable(indexId)
73+
idxFiles.add(getIdxFile(indexId))
74+
75+
if (indexTable.exists().not()) {
76+
store.addIndex(Js5Index.default(indexId))
77+
latch.countDown()
78+
return
79+
}
80+
81+
val indexDatTable = datFile.readReferenceTable(masterIdxFile.id(), indexTable)
82+
store.addIndex(indexTable.loadIndex(datFile, getIdxFile(indexId), ByteBuffer.wrap(indexDatTable).whirlpool(), ContainerCodec.decompress(indexDatTable)))
83+
latch.countDown()
8784
}
8885

8986
override fun loadMasterReferenceTable(groupId: Int): ByteArray {
@@ -102,7 +99,7 @@ internal class Js5DiskStorage(
10299

103100
private fun getIdxFile(id: Int): IdxFile {
104101
idxFiles.find { it.id() == id }?.let { return it }
105-
return IdxFile(id, File("$path/${Constants.MAIN_FILE_IDX}${id}"))
102+
return IdxFile(id, Path.of("$path/${Constants.MAIN_FILE_IDX}${id}"))
106103
}
107104

108105
override fun close() {

cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/DatFile.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import com.runetopic.cache.exception.EndOfDatFileException
55
import com.runetopic.cache.hierarchy.ReferenceTable
66
import com.runetopic.cache.store.Constants.SECTOR_SIZE
77
import com.runetopic.cache.store.storage.js5.IDatFile
8-
import java.io.File
98
import java.io.RandomAccessFile
109
import java.nio.ByteBuffer
10+
import java.nio.file.Path
1111

1212
/**
1313
* @author Tyler Telis
1414
* @email <[email protected]>
1515
*/
1616
internal class DatFile(
17-
file: File
17+
path: Path
1818
): IDatFile {
19-
private val datFile: RandomAccessFile = RandomAccessFile(file, "rw")
19+
private val datFile: RandomAccessFile = RandomAccessFile(path.toFile(), "rw")
2020

2121
@Synchronized
2222
override fun readReferenceTable(id: Int, referenceTable: ReferenceTable): ByteArray {
@@ -107,7 +107,7 @@ internal class DatFile(
107107
currentId: Int,
108108
) {
109109
if (referenceTableId != currentReferenceTableId || currentPart != part || id != currentId) {
110-
throw DatFileException("DataFile mismatch Id={${currentId}} != {${id}}, ReferenceTableId={${currentReferenceTableId}} != {${referenceTableId}}, CurrentPart={${currentPart}} != {${part}}")
110+
throw DatFileException("DataFile mismatch Id={${currentId}} != {${id}}, ReferenceTableId={${currentReferenceTableId}} != {${referenceTableId}}, CurrentPart={${currentPart}} != {${part}}")
111111
}
112112
}
113113

0 commit comments

Comments
 (0)