Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Robin Randhawa
redoxfs
Commits
16d6f6b9
Commit
16d6f6b9
authored
Nov 19, 2017
by
Jeremy Soller
Browse files
Simplify disk cache
parent
2dd4c920
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
src/disk/cache
/mod
.rs
→
src/disk/cache.rs
View file @
16d6f6b9
use
std
::{
cmp
,
ptr
};
use
std
::
collections
::{
BTreeMap
,
VecDeque
};
use
syscall
::
error
::
Result
;
use
BLOCK_SIZE
;
use
disk
::
Disk
;
use
self
::
lru_cache
::
LruCache
;
mod
linked_hash_map
;
mod
lru_cache
;
fn
copy_memory
(
src
:
&
[
u8
],
dest
:
&
mut
[
u8
])
->
usize
{
let
len
=
cmp
::
min
(
src
.len
(),
dest
.len
());
unsafe
{
ptr
::
copy
(
src
.as_ptr
(),
dest
.as_mut_ptr
(),
len
)
};
...
...
@@ -17,15 +13,29 @@ fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
pub
struct
DiskCache
<
T
>
{
inner
:
T
,
cache
:
LruCache
<
u64
,
[
u8
;
BLOCK_SIZE
as
usize
]
>
,
cache
:
BTreeMap
<
u64
,
[
u8
;
BLOCK_SIZE
as
usize
]
>
,
order
:
VecDeque
<
u64
>
,
size
:
usize
,
}
impl
<
T
:
Disk
>
DiskCache
<
T
>
{
pub
fn
new
(
inner
:
T
)
->
Self
{
DiskCache
{
inner
:
inner
,
cache
:
LruCache
::
new
((
256
*
1024
*
1024
/
BLOCK_SIZE
)
as
usize
)
// 256 MB cache
cache
:
BTreeMap
::
new
(),
order
:
VecDeque
::
new
(),
size
:
65536
,
// 256 MB cache
}
}
fn
insert
(
&
mut
self
,
i
:
u64
,
data
:
[
u8
;
BLOCK_SIZE
as
usize
])
{
while
self
.order
.len
()
>=
self
.size
{
let
removed
=
self
.order
.pop_front
()
.unwrap
();
self
.cache
.remove
(
&
removed
);
}
self
.cache
.insert
(
i
,
data
);
self
.order
.push_back
(
i
);
}
}
...
...
@@ -63,7 +73,7 @@ impl<T: Disk> Disk for DiskCache<T> {
let
mut
cache_buf
=
[
0
;
BLOCK_SIZE
as
usize
];
read
+=
copy_memory
(
buffer_slice
,
&
mut
cache_buf
);
self
.
cache
.
insert
(
block_i
,
cache_buf
);
self
.insert
(
block_i
,
cache_buf
);
}
}
...
...
@@ -85,7 +95,7 @@ impl<T: Disk> Disk for DiskCache<T> {
let
mut
cache_buf
=
[
0
;
BLOCK_SIZE
as
usize
];
written
+=
copy_memory
(
buffer_slice
,
&
mut
cache_buf
);
self
.
cache
.
insert
(
block_i
,
cache_buf
);
self
.insert
(
block_i
,
cache_buf
);
}
Ok
(
written
)
...
...
src/disk/cache/linked_hash_map.rs
deleted
100644 → 0
View file @
2dd4c920
This diff is collapsed.
Click to expand it.
src/disk/cache/lru_cache.rs
deleted
100644 → 0
View file @
2dd4c920
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A cache that holds a limited number of key-value pairs. When the
//! capacity of the cache is exceeded, the least-recently-used
//! (where "used" means a look-up or putting the pair into the cache)
//! pair is automatically removed.
use
std
::
collections
::
hash_map
::
RandomState
;
use
std
::
fmt
;
use
std
::
hash
::{
Hash
,
BuildHasher
};
use
std
::
borrow
::
Borrow
;
use
super
::
linked_hash_map
::{
self
,
LinkedHashMap
};
// FIXME(conventions): implement indexing?
/// An LRU cache.
pub
struct
LruCache
<
K
,
V
,
S
=
RandomState
>
where
K
:
Eq
+
Hash
,
S
:
BuildHasher
{
map
:
LinkedHashMap
<
K
,
V
,
S
>
,
max_size
:
usize
,
}
impl
<
K
:
Hash
+
Eq
,
V
>
LruCache
<
K
,
V
>
{
/// Creates an empty cache that can hold at most `capacity` items.
pub
fn
new
(
capacity
:
usize
)
->
LruCache
<
K
,
V
>
{
LruCache
{
map
:
LinkedHashMap
::
new
(),
max_size
:
capacity
,
}
}
}
impl
<
K
,
V
,
S
>
LruCache
<
K
,
V
,
S
>
where
K
:
Eq
+
Hash
,
S
:
BuildHasher
{
/// Creates an empty cache that can hold at most `capacity` items with the given hash state.
pub
fn
with_hash_state
(
capacity
:
usize
,
hash_state
:
S
)
->
LruCache
<
K
,
V
,
S
>
{
LruCache
{
map
:
LinkedHashMap
::
with_hash_state
(
hash_state
),
max_size
:
capacity
}
}
/// Checks if the map contains the given key.
pub
fn
contains_key
<
Q
:
?
Sized
>
(
&
mut
self
,
key
:
&
Q
)
->
bool
where
K
:
Borrow
<
Q
>
,
Q
:
Hash
+
Eq
{
self
.get_mut
(
key
)
.is_some
()
}
/// Inserts a key-value pair into the cache. If the key already existed, the old value is
/// returned.
pub
fn
insert
(
&
mut
self
,
k
:
K
,
v
:
V
)
->
Option
<
V
>
{
let
old_val
=
self
.map
.insert
(
k
,
v
);
if
self
.len
()
>
self
.capacity
()
{
self
.remove_lru
();
}
old_val
}
/// Returns a mutable reference to the value corresponding to the given key in the cache, if
/// any.
pub
fn
get_mut
<
Q
:
?
Sized
>
(
&
mut
self
,
k
:
&
Q
)
->
Option
<&
mut
V
>
where
K
:
Borrow
<
Q
>
,
Q
:
Hash
+
Eq
{
self
.map
.get_refresh
(
k
)
}
/// Removes the given key from the cache and returns its corresponding value.
pub
fn
remove
<
Q
:
?
Sized
>
(
&
mut
self
,
k
:
&
Q
)
->
Option
<
V
>
where
K
:
Borrow
<
Q
>
,
Q
:
Hash
+
Eq
{
self
.map
.remove
(
k
)
}
/// Returns the maximum number of key-value pairs the cache can hold.
pub
fn
capacity
(
&
self
)
->
usize
{
self
.max_size
}
/// Sets the number of key-value pairs the cache can hold. Removes
/// least-recently-used key-value pairs if necessary.
pub
fn
set_capacity
(
&
mut
self
,
capacity
:
usize
)
{
for
_
in
capacity
..
self
.len
()
{
self
.remove_lru
();
}
self
.max_size
=
capacity
;
}
#[inline]
fn
remove_lru
(
&
mut
self
)
->
Option
<
(
K
,
V
)
>
{
self
.map
.pop_front
()
}
/// Returns the number of key-value pairs in the cache.
pub
fn
len
(
&
self
)
->
usize
{
self
.map
.len
()
}
/// Returns `true` if the cache contains no key-value pairs.
pub
fn
is_empty
(
&
self
)
->
bool
{
self
.map
.is_empty
()
}
/// Removes all key-value pairs from the cache.
pub
fn
clear
(
&
mut
self
)
{
self
.map
.clear
();
}
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order.
///
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
pub
fn
iter
(
&
self
)
->
Iter
<
K
,
V
>
{
Iter
(
self
.map
.iter
())
}
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order,
/// with mutable references to the values.
///
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
pub
fn
iter_mut
(
&
mut
self
)
->
IterMut
<
K
,
V
>
{
IterMut
(
self
.map
.iter_mut
())
}
}
impl
<
K
:
Hash
+
Eq
,
V
,
S
:
BuildHasher
>
Extend
<
(
K
,
V
)
>
for
LruCache
<
K
,
V
,
S
>
{
fn
extend
<
T
:
IntoIterator
<
Item
=
(
K
,
V
)
>>
(
&
mut
self
,
iter
:
T
)
{
for
(
k
,
v
)
in
iter
{
self
.insert
(
k
,
v
);
}
}
}
impl
<
A
:
fmt
::
Debug
+
Hash
+
Eq
,
B
:
fmt
::
Debug
,
S
:
BuildHasher
>
fmt
::
Debug
for
LruCache
<
A
,
B
,
S
>
{
fn
fmt
(
&
self
,
f
:
&
mut
fmt
::
Formatter
)
->
fmt
::
Result
{
f
.debug_map
()
.entries
(
self
.iter
()
.rev
())
.finish
()
}
}
impl
<
'a
,
K
,
V
,
S
>
IntoIterator
for
&
'a
LruCache
<
K
,
V
,
S
>
where
K
:
Eq
+
Hash
,
S
:
BuildHasher
{
type
Item
=
(
&
'a
K
,
&
'a
V
);
type
IntoIter
=
Iter
<
'a
,
K
,
V
>
;
fn
into_iter
(
self
)
->
Iter
<
'a
,
K
,
V
>
{
self
.iter
()
}
}
impl
<
'a
,
K
,
V
,
S
>
IntoIterator
for
&
'a
mut
LruCache
<
K
,
V
,
S
>
where
K
:
Eq
+
Hash
,
S
:
BuildHasher
{
type
Item
=
(
&
'a
K
,
&
'a
mut
V
);
type
IntoIter
=
IterMut
<
'a
,
K
,
V
>
;
fn
into_iter
(
self
)
->
IterMut
<
'a
,
K
,
V
>
{
self
.iter_mut
()
}
}
impl
<
K
,
V
>
Clone
for
LruCache
<
K
,
V
>
where
K
:
Clone
+
Eq
+
Hash
,
V
:
Clone
{
fn
clone
(
&
self
)
->
LruCache
<
K
,
V
>
{
LruCache
{
map
:
self
.map
.clone
(),
..*
self
}
}
}
/// An iterator over a cache's key-value pairs in least- to most-recently-used order.
///
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
pub
struct
Iter
<
'a
,
K
:
'a
,
V
:
'a
>
(
linked_hash_map
::
Iter
<
'a
,
K
,
V
>
);
impl
<
'a
,
K
,
V
>
Clone
for
Iter
<
'a
,
K
,
V
>
{
fn
clone
(
&
self
)
->
Iter
<
'a
,
K
,
V
>
{
Iter
(
self
.0
.clone
())
}
}
impl
<
'a
,
K
,
V
>
Iterator
for
Iter
<
'a
,
K
,
V
>
{
type
Item
=
(
&
'a
K
,
&
'a
V
);
fn
next
(
&
mut
self
)
->
Option
<
(
&
'a
K
,
&
'a
V
)
>
{
self
.0
.next
()
}
fn
size_hint
(
&
self
)
->
(
usize
,
Option
<
usize
>
)
{
self
.0
.size_hint
()
}
}
impl
<
'a
,
K
,
V
>
DoubleEndedIterator
for
Iter
<
'a
,
K
,
V
>
{
fn
next_back
(
&
mut
self
)
->
Option
<
(
&
'a
K
,
&
'a
V
)
>
{
self
.0
.next_back
()
}
}
impl
<
'a
,
K
,
V
>
ExactSizeIterator
for
Iter
<
'a
,
K
,
V
>
{
fn
len
(
&
self
)
->
usize
{
self
.0
.len
()
}
}
/// An iterator over a cache's key-value pairs in least- to most-recently-used order with mutable
/// references to the values.
///
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
pub
struct
IterMut
<
'a
,
K
:
'a
,
V
:
'a
>
(
linked_hash_map
::
IterMut
<
'a
,
K
,
V
>
);
impl
<
'a
,
K
,
V
>
Iterator
for
IterMut
<
'a
,
K
,
V
>
{
type
Item
=
(
&
'a
K
,
&
'a
mut
V
);
fn
next
(
&
mut
self
)
->
Option
<
(
&
'a
K
,
&
'a
mut
V
)
>
{
self
.0
.next
()
}
fn
size_hint
(
&
self
)
->
(
usize
,
Option
<
usize
>
)
{
self
.0
.size_hint
()
}
}
impl
<
'a
,
K
,
V
>
DoubleEndedIterator
for
IterMut
<
'a
,
K
,
V
>
{
fn
next_back
(
&
mut
self
)
->
Option
<
(
&
'a
K
,
&
'a
mut
V
)
>
{
self
.0
.next_back
()
}
}
impl
<
'a
,
K
,
V
>
ExactSizeIterator
for
IterMut
<
'a
,
K
,
V
>
{
fn
len
(
&
self
)
->
usize
{
self
.0
.len
()
}
}
#[cfg(test)]
mod
tests
{
use
super
::
LruCache
;
#[test]
fn
test_put_and_get
()
{
let
mut
cache
=
LruCache
::
new
(
2
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
assert_eq!
(
cache
.get_mut
(
&
1
),
Some
(
&
mut
10
));
assert_eq!
(
cache
.get_mut
(
&
2
),
Some
(
&
mut
20
));
assert_eq!
(
cache
.len
(),
2
);
}
#[test]
fn
test_put_update
()
{
let
mut
cache
=
LruCache
::
new
(
1
);
cache
.insert
(
"1"
,
10
);
cache
.insert
(
"1"
,
19
);
assert_eq!
(
cache
.get_mut
(
"1"
),
Some
(
&
mut
19
));
assert_eq!
(
cache
.len
(),
1
);
}
#[test]
fn
test_contains_key
()
{
let
mut
cache
=
LruCache
::
new
(
1
);
cache
.insert
(
"1"
,
10
);
assert_eq!
(
cache
.contains_key
(
"1"
),
true
);
}
#[test]
fn
test_expire_lru
()
{
let
mut
cache
=
LruCache
::
new
(
2
);
cache
.insert
(
"foo1"
,
"bar1"
);
cache
.insert
(
"foo2"
,
"bar2"
);
cache
.insert
(
"foo3"
,
"bar3"
);
assert
!
(
cache
.get_mut
(
"foo1"
)
.is_none
());
cache
.insert
(
"foo2"
,
"bar2update"
);
cache
.insert
(
"foo4"
,
"bar4"
);
assert
!
(
cache
.get_mut
(
"foo3"
)
.is_none
());
}
#[test]
fn
test_pop
()
{
let
mut
cache
=
LruCache
::
new
(
2
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
assert_eq!
(
cache
.len
(),
2
);
let
opt1
=
cache
.remove
(
&
1
);
assert
!
(
opt1
.is_some
());
assert_eq!
(
opt1
.unwrap
(),
10
);
assert
!
(
cache
.get_mut
(
&
1
)
.is_none
());
assert_eq!
(
cache
.len
(),
1
);
}
#[test]
fn
test_change_capacity
()
{
let
mut
cache
=
LruCache
::
new
(
2
);
assert_eq!
(
cache
.capacity
(),
2
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
cache
.set_capacity
(
1
);
assert
!
(
cache
.get_mut
(
&
1
)
.is_none
());
assert_eq!
(
cache
.capacity
(),
1
);
}
#[test]
fn
test_debug
()
{
let
mut
cache
=
LruCache
::
new
(
3
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
cache
.insert
(
3
,
30
);
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{3: 30, 2: 20, 1: 10}"
);
cache
.insert
(
2
,
22
);
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{2: 22, 3: 30, 1: 10}"
);
cache
.insert
(
6
,
60
);
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{6: 60, 2: 22, 3: 30}"
);
cache
.get_mut
(
&
3
);
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{3: 30, 6: 60, 2: 22}"
);
cache
.set_capacity
(
2
);
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{3: 30, 6: 60}"
);
}
#[test]
fn
test_remove
()
{
let
mut
cache
=
LruCache
::
new
(
3
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
cache
.insert
(
3
,
30
);
cache
.insert
(
4
,
40
);
cache
.insert
(
5
,
50
);
cache
.remove
(
&
3
);
cache
.remove
(
&
4
);
assert
!
(
cache
.get_mut
(
&
3
)
.is_none
());
assert
!
(
cache
.get_mut
(
&
4
)
.is_none
());
cache
.insert
(
6
,
60
);
cache
.insert
(
7
,
70
);
cache
.insert
(
8
,
80
);
assert
!
(
cache
.get_mut
(
&
5
)
.is_none
());
assert_eq!
(
cache
.get_mut
(
&
6
),
Some
(
&
mut
60
));
assert_eq!
(
cache
.get_mut
(
&
7
),
Some
(
&
mut
70
));
assert_eq!
(
cache
.get_mut
(
&
8
),
Some
(
&
mut
80
));
}
#[test]
fn
test_clear
()
{
let
mut
cache
=
LruCache
::
new
(
2
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
cache
.clear
();
assert
!
(
cache
.get_mut
(
&
1
)
.is_none
());
assert
!
(
cache
.get_mut
(
&
2
)
.is_none
());
assert_eq!
(
format!
(
"{:?}"
,
cache
),
"{}"
);
}
#[test]
fn
test_iter
()
{
let
mut
cache
=
LruCache
::
new
(
3
);
cache
.insert
(
1
,
10
);
cache
.insert
(
2
,
20
);
cache
.insert
(
3
,
30
);
cache
.insert
(
4
,
40
);
cache
.insert
(
5
,
50
);
assert_eq!
(
cache
.iter
()
.collect
::
<
Vec
<
_
>>
(),
[(
&
3
,
&
30
),
(
&
4
,
&
40
),
(
&
5
,
&
50
)]);
assert_eq!
(
cache
.iter_mut
()
.collect
::
<
Vec
<
_
>>
(),
[(
&
3
,
&
mut
30
),
(
&
4
,
&
mut
40
),
(
&
5
,
&
mut
50
)]);
assert_eq!
(
cache
.iter
()
.rev
()
.collect
::
<
Vec
<
_
>>
(),
[(
&
5
,
&
50
),
(
&
4
,
&
40
),
(
&
3
,
&
30
)]);
assert_eq!
(
cache
.iter_mut
()
.rev
()
.collect
::
<
Vec
<
_
>>
(),
[(
&
5
,
&
mut
50
),
(
&
4
,
&
mut
40
),
(
&
3
,
&
mut
30
)]);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment