Skip to content

Commit 989c5f8

Browse files
committed
Checksum::validate now accepts either by borrow or by value
Additionally, made some example and doc improvements
1 parent 9704d1c commit 989c5f8

5 files changed

Lines changed: 167 additions & 111 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rs-cache"
3-
version = "0.8.7" # Remember to update usage
3+
version = "0.8.8" # Remember to update usage
44
authors = ["jimvdl <jimvdlind@gmail.com>"]
55
edition = "2021"
66
license = "MIT"

README.md

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ Useful links:\
3232

3333
## Safety
3434

35-
In order to read bytes in a high performant way the cache uses [memmap2](https://crates.io/crates/memmap2). This can be unsafe because of its potential for _Undefined Behaviour_ when the underlying file is subsequently modified, in or out of process. Using `Mmap` here is safe because the RuneScape cache is a read-only binary file system. The map will remain valid even after the `File` is dropped, it's completely independent of the `File` used to create it. Therefore, the use of unsafe is not propagated outwards. When the `Cache` is dropped memory will be subsequently unmapped.
35+
In order to read bytes in a high performant way the cache uses
36+
[memmap2](https://crates.io/crates/memmap2). This can be unsafe because of its potential for
37+
_Undefined Behaviour_ when the underlying file is subsequently modified, in or out of process.
38+
39+
Using `Mmap` here is safe because the RuneScape cache is a read-only binary file system. The map will remain valid even after the `File` is dropped, it's completely independent of the `File` used to create it. Therefore, the use of unsafe is not propagated outwards. When the `Cache` is dropped memory will be subsequently unmapped.
3640

3741
## Features
3842

@@ -41,50 +45,63 @@ A lot of types derive [serde](https://crates.io/crates/serde)'s `Serialize` and
4145

4246
## Quick Start
4347

44-
For an instance that stays local to this thread you can simply use:
48+
The recommended usage would be to wrap it using
49+
[`std::sync::LazyLock`](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) making it the
50+
easiest way to access cache data from anywhere and at any time. No need for an `Arc` or a `Mutex`
51+
because `Cache` will always be `Send + Sync`.
4552
```rust
4653
use rscache::Cache;
54+
use std::sync::LazyLock;
4755

48-
let cache = Cache::new("./data/osrs_cache").unwrap();
56+
static CACHE: LazyLock<Cache> = LazyLock::new(|| {
57+
Cache::new("./data/osrs_cache")
58+
.expect("cache files to be successfully memory mapped")
59+
});
4960

50-
let index_id = 2; // Config index.
51-
let archive_id = 10; // Archive containing item definitions.
61+
std::thread::spawn(|| -> Result<(), rscache::Error> {
62+
let buffer = CACHE.read(0, 10)?;
63+
Ok(())
64+
});
5265

53-
let buffer = cache.read(index_id, archive_id).unwrap();
66+
std::thread::spawn(|| -> Result<(), rscache::Error> {
67+
let buffer = CACHE.read(0, 10)?;
68+
Ok(())
69+
});
5470
```
5571

56-
If you want to share the instance over multiple threads you can do so by wrapping it in an [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
72+
For an instance that stays local to this thread you can simply use:
5773
```rust
5874
use rscache::Cache;
59-
use std::sync::Arc;
6075

61-
let cache = Arc::new(Cache::new("./data/osrs_cache").unwrap());
76+
let cache = Cache::new("./data/osrs_cache")
77+
.expect("cache files to be successfully memory mapped");
6278

63-
let c = Arc::clone(&cache);
64-
std::thread::spawn(move || {
65-
c.read(0, 10).unwrap();
66-
});
79+
let index_id = 2; // Config index.
80+
let archive_id = 10; // Archive containing item definitions.
6781

68-
std::thread::spawn(move || {
69-
cache.read(0, 10).unwrap();
70-
});
82+
let buffer = cache.read(index_id, archive_id)?;
7183
```
7284

73-
The recommended usage would be to wrap it using [`once_cell`](https://docs.rs/once_cell/latest/once_cell/) making it the easiest way to access cache data from anywhere and at any time. No need for an `Arc` or a `Mutex` because `Cache` will always be `Send` & `Sync`.
85+
If you want to share the instance over multiple threads you can do so by wrapping it in an
86+
[`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
7487
```rust
7588
use rscache::Cache;
76-
use once_cell::sync::Lazy;
77-
78-
static CACHE: Lazy<Cache> = Lazy::new(|| {
79-
Cache::new("./data/osrs_cache").unwrap()
80-
});
89+
use std::sync::Arc;
8190

82-
std::thread::spawn(move || {
83-
CACHE.read(0, 10).unwrap();
91+
let cache = Arc::new(Cache::new("./data/osrs_cache")
92+
.expect("cache files to be successfully memory mapped"));
93+
94+
let c = Arc::clone(&cache);
95+
std::thread::spawn(move || -> Result<(), rscache::Error> {
96+
// use the cloned handle
97+
let buffer = c.read(0, 10)?;
98+
Ok(())
8499
});
85-
86-
std::thread::spawn(move || {
87-
CACHE.read(0, 10).unwrap();
100+
101+
std::thread::spawn(move || -> Result<(), rscache::Error> {
102+
// use handle directly and take ownership
103+
let buffer = cache.read(0, 10)?;
104+
Ok(())
88105
});
89106
```
90107

@@ -99,7 +116,7 @@ Add this to your `Cargo.toml`:
99116

100117
```toml
101118
[dependencies]
102-
rs-cache = "0.8.6"
119+
rs-cache = "0.8.8"
103120
```
104121

105122
Examples can be found in the [examples](examples/) directory which include both update protocols.

src/checksum.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//! # }
2424
//! ```
2525
26-
use std::iter::IntoIterator;
26+
use std::{borrow::Borrow, iter::IntoIterator};
2727
use std::slice::Iter;
2828

2929
use crate::{error::ValidateError, Cache};
@@ -123,7 +123,7 @@ impl Checksum {
123123
///
124124
/// Note: It defaults to OSRS. RS3 clients use RSA to encrypt
125125
/// network traffic, which includes the checksum. When encoding for RS3 clients
126-
/// use [`RsaChecksum`](RsaChecksum) instead.
126+
/// use [`RsaChecksum`] instead.
127127
///
128128
/// After encoding the checksum it can be sent to the client.
129129
///
@@ -165,13 +165,32 @@ impl Checksum {
165165

166166
/// Validates the given crcs from the client with the internal crcs of this cache.
167167
///
168+
/// ```
169+
/// # use rscache::{Cache, error::Error};
170+
/// # use rscache::checksum::Checksum;
171+
/// # fn main() -> Result<(), Error> {
172+
/// # let cache = Cache::new("./data/osrs_cache")?;
173+
/// let checksum = Checksum::new(&cache)?;
174+
///
175+
/// let crcs = [
176+
/// 1593884597, 1029608590, 16840364, 4209099954, 3716821437, 165713182, 686540367, 4262755489,
177+
/// 2208636505, 3047082366, 586413816, 2890424900, 3411535427, 3178880569, 153718440,
178+
/// 3849392898, 3628627685, 2813112885, 1461700456, 2751169400, 2927815226,
179+
/// ];
180+
///
181+
/// assert!(checksum.validate(crcs).is_ok());
182+
/// # Ok(())
183+
/// # }
184+
/// ```
185+
///
168186
/// # Errors
169187
///
170188
/// When the lengths of the crc iterators don't match up because too many or too few indices
171189
/// were shared between the client and the server, or if a crc value mismatches.
172-
pub fn validate<'b, I>(&self, crcs: I) -> Result<(), ValidateError>
190+
pub fn validate<I>(&self, crcs: I) -> Result<(), ValidateError>
173191
where
174-
I: IntoIterator<Item = &'b u32>,
192+
I: IntoIterator,
193+
I::Item: Borrow<u32>,
175194
<I as IntoIterator>::IntoIter: ExactSizeIterator,
176195
{
177196
let crcs = crcs.into_iter();
@@ -189,11 +208,11 @@ impl Checksum {
189208
.zip(crcs)
190209
.enumerate()
191210
{
192-
if internal != external {
211+
if internal != external.borrow() {
193212
return Err(ValidateError::InvalidCrc {
194213
idx: index,
195214
internal: *internal,
196-
external: *external,
215+
external: *external.borrow(),
197216
});
198217
}
199218
}

src/lib.rs

Lines changed: 79 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,118 @@
11
//! A read-only, high-level, virtual file API for the RuneScape cache.
22
//!
3-
//! This crate provides high performant data reads into the [Oldschool
4-
//! RuneScape] and [RuneScape 3] cache file systems. It can read the necessary
5-
//! data to synchronize the client's cache with the server. There are also some
6-
//! [loaders](#loaders) that give access to definitions from the cache such as
7-
//! items or npcs.
3+
//! This crate provides high performant data reads into the [Oldschool RuneScape] and [RuneScape 3]
4+
//! cache file systems. It can read the necessary data to synchronize the client's cache with the
5+
//! server. There are also some [loaders](#loaders) that give access to definitions from the cache
6+
//! such as items or npcs.
87
//!
9-
//! For read-heavy workloads, a writer can be used to prevent continuous buffer
10-
//! allocations. By default every read will allocate a writer with the correct
11-
//! capacity.
8+
//! For read-heavy workloads, a writer can be used to prevent continuous buffer allocations. By
9+
//! default every read will allocate a writer with the correct capacity.
1210
//!
13-
//! RuneScape's chat system uses huffman coding to compress messages. In order
14-
//! to decompress them this library has a [`Huffman`] implementation.
11+
//! RuneScape's chat system uses huffman coding to compress messages. In order to decompress them
12+
//! this library has a [`Huffman`] implementation.
1513
//!
16-
//! When a RuneScape client sends game packets the id's are encoded and can be
17-
//! decoded with the [`IsaacRand`] implementation. These id's are encoded by the
18-
//! client in a predictable random order which can be reversed if the server has
19-
//! its own `IsaacRand` with the same encoder/decoder keys. These keys are sent
20-
//! by the client on login and are user specific. It will only send encoded
14+
//! When a RuneScape client sends game packets the id's are encoded and can be decoded with the
15+
//! [`IsaacRand`] implementation. These id's are encoded by the client in a predictable random order
16+
//! which can be reversed if the server has its own `IsaacRand` with the same encoder/decoder keys.
17+
//! These keys are sent by the client on login and are user specific. It will only send encoded
2118
//! packet id's if the packets are game packets.
2219
//!
23-
//! Note that this crate is still evolving; both OSRS & RS3 are not fully
24-
//! supported/implemented and will probably contain bugs or miss core features.
25-
//! If you require features or find bugs consider [opening an issue].
20+
//! Note that this crate is still evolving; both OSRS & RS3 are not fully supported/implemented and
21+
//! will probably contain bugs or miss core features. If you require features or find bugs consider
22+
//! [opening an issue].
2623
//!
2724
//! # Safety
2825
//!
29-
//! In order to read bytes in a high performant way the cache uses [memmap2].
30-
//! This can be unsafe because of its potential for _Undefined Behaviour_ when
31-
//! the underlying file is subsequently modified, in or out of process. Using
32-
//! `Mmap` here is safe because the RuneScape cache is a read-only binary file
33-
//! system. The map will remain valid even after the `File` is dropped, it's
34-
//! completely independent of the `File` used to create it. Therefore, the use
35-
//! of unsafe is not propagated outwards. When the `Cache` is dropped memory
36-
//! will be subsequently unmapped.
26+
//! In order to read bytes in a high performant way the cache uses [memmap2]. This can be unsafe
27+
//! because of its potential for _Undefined Behaviour_ when the underlying file is subsequently
28+
//! modified, in or out of process.
29+
//!
30+
//! Using `Mmap` here is safe because the RuneScape cache is a read-only binary file system. The map
31+
//! will remain valid even after the `File` is dropped, it's completely independent of the `File`
32+
//! used to create it. Therefore, the use of unsafe is not propagated outwards. When the `Cache` is
33+
//! dropped memory will be subsequently unmapped.
3734
//!
3835
//! # Features
3936
//!
40-
//! The cache's protocol defaults to OSRS. In order to use the RS3 protocol you
41-
//! can enable the `rs3` feature flag. A lot of types derive [serde]'s
42-
//! `Serialize` and `Deserialize`. The `serde-derive` feature flag can be used
43-
//! to enable (de)serialization on any compatible types.
37+
//! The cache's protocol defaults to OSRS. In order to use the RS3 protocol you can enable the `rs3`
38+
//! feature flag. A lot of types derive [serde]'s `Serialize` and `Deserialize`. The `serde-derive`
39+
//! feature flag can be used to enable (de)serialization on any compatible types.
4440
//!
4541
//! # Quick Start
4642
//!
43+
//! The recommended usage would be to wrap it using
44+
//! [`std::sync::LazyLock`](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) making it the
45+
//! easiest way to access cache data from anywhere and at any time. No need for an `Arc` or a
46+
//! `Mutex` because `Cache` will always be `Send + Sync`.
47+
//! ```rust
48+
//! use rscache::Cache;
49+
//! use std::sync::LazyLock;
50+
//!
51+
//! static CACHE: LazyLock<Cache> = LazyLock::new(|| {
52+
//! Cache::new("./data/osrs_cache")
53+
//! .expect("cache files to be successfully memory mapped")
54+
//! });
55+
//!
56+
//! std::thread::spawn(|| -> Result<(), rscache::Error> {
57+
//! let buffer = CACHE.read(0, 10)?;
58+
//! Ok(())
59+
//! });
60+
//!
61+
//! std::thread::spawn(|| -> Result<(), rscache::Error> {
62+
//! let buffer = CACHE.read(0, 10)?;
63+
//! Ok(())
64+
//! });
65+
//! ```
66+
//!
4767
//! For an instance that stays local to this thread you can simply use:
4868
//! ```
4969
//! use rscache::Cache;
50-
//!
51-
//! let cache = Cache::new("./data/osrs_cache").unwrap();
52-
//!
70+
//!
71+
//! # fn main() -> Result<(), rscache::Error> {
72+
//! let cache = Cache::new("./data/osrs_cache")
73+
//! .expect("cache files to be successfully memory mapped");
74+
//!
5375
//! let index_id = 2; // Config index.
5476
//! let archive_id = 10; // Archive containing item definitions.
55-
//!
56-
//! let buffer = cache.read(index_id, archive_id).unwrap();
77+
//!
78+
//! let buffer = cache.read(index_id, archive_id)?;
79+
//! # Ok(())
80+
//! # }
5781
//! ```
58-
//!
59-
//! If you want to share the instance over multiple threads you can do so by
60-
//! wrapping it in an
82+
//!
83+
//! If you want to share the instance over multiple threads you can do so by wrapping it in an
6184
//! [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
6285
//! ```
6386
//! use rscache::Cache;
6487
//! use std::sync::Arc;
65-
//!
66-
//! let cache = Arc::new(Cache::new("./data/osrs_cache").unwrap());
88+
//!
89+
//! let cache = Arc::new(Cache::new("./data/osrs_cache")
90+
//! .expect("cache files to be successfully memory mapped"));
6791
//!
6892
//! let c = Arc::clone(&cache);
69-
//! std::thread::spawn(move || {
70-
//! c.read(0, 10).unwrap();
93+
//! std::thread::spawn(move || -> Result<(), rscache::Error> {
94+
//! // use the cloned handle
95+
//! let buffer = c.read(0, 10)?;
96+
//! Ok(())
7197
//! });
7298
//!
73-
//! std::thread::spawn(move || {
74-
//! cache.read(0, 10).unwrap();
75-
//! });
76-
//! ```
77-
//!
78-
//! The recommended usage would be to wrap it using
79-
//! [`once_cell`](https://docs.rs/once_cell/latest/once_cell/) making it the
80-
//! easiest way to access cache data from anywhere and at any time. No need for
81-
//! an `Arc` or a `Mutex` because `Cache` will always be `Send` & `Sync`.
82-
//! ```
83-
//! use rscache::Cache;
84-
//! use once_cell::sync::Lazy;
85-
//!
86-
//! static CACHE: Lazy<Cache> = Lazy::new(|| {
87-
//! Cache::new("./data/osrs_cache").unwrap()
88-
//! });
89-
//!
90-
//! std::thread::spawn(move || {
91-
//! CACHE.read(0, 10).unwrap();
92-
//! });
93-
//!
94-
//! std::thread::spawn(move || {
95-
//! CACHE.read(0, 10).unwrap();
99+
//! std::thread::spawn(move || -> Result<(), rscache::Error> {
100+
//! // use handle directly and take ownership
101+
//! let buffer = cache.read(0, 10)?;
102+
//! Ok(())
96103
//! });
97104
//! ```
98105
//!
99106
//! # Loaders
100107
//!
101-
//! In order to get [definitions](crate::definition) you can look at the
102-
//! [loaders](crate::loader) this library provides. The loaders use the cache as
103-
//! a dependency to parse in their data and cache the relevant definitions
104-
//! internally. The loader module also tells you how to make a loader if this
105-
//! crate doesn't (yet) provide it.
108+
//! In order to get [definitions](crate::definition) you can look at the [loaders](crate::loader)
109+
//! this library provides. The loaders use the cache as a dependency to parse in their data and
110+
//! cache the relevant definitions internally. The loader module also tells you how to make a loader
111+
//! if this crate doesn't (yet) provide it.
106112
//!
107-
//! Note: Some loaders cache these definitions lazily because of either the size
108-
//! of the data or the performance. The map loader for example is both slow and
109-
//! large so caching is by default lazy. Lazy loaders require mutability.
113+
//! Note: Some loaders cache these definitions lazily because of either the size of the data or the
114+
//! performance. The map loader for example is both slow and large so caching is by default lazy.
115+
//! Lazy loaders require mutability.
110116
//!
111117
//! [Oldschool RuneScape]: https://oldschool.runescape.com/
112118
//! [RuneScape 3]: https://www.runescape.com/

0 commit comments

Comments
 (0)