Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
parity
Mirrored projects
ink
Commits
be555eb9
Commit
be555eb9
authored
Jan 23, 2019
by
Hero Bird
Browse files
[pdsl_core] Implement flushing for SyncCell and SyncChunk
parent
db20a6cf
Changes
10
Hide whitespace changes
Inline
Side-by-side
pdsl_core/src/storage/cell/sync_cell.rs
View file @
be555eb9
...
...
@@ -369,7 +369,6 @@ where
{
/// Removes the value from the cell.
pub
fn
clear
(
&
mut
self
)
{
self
.cell
.clear
();
// TODO: Removes this after implementation of flushing
self
.cache
.update
(
None
);
self
.cache
.mark_dirty
();
}
...
...
@@ -395,7 +394,6 @@ where
{
/// Sets the value of the cell.
pub
fn
set
(
&
mut
self
,
val
:
T
)
{
self
.cell
.store
(
&
val
);
// TODO: Removes this after implementation of flushing
self
.cache
.update
(
Some
(
val
));
self
.cache
.mark_dirty
();
}
...
...
@@ -434,16 +432,19 @@ where
#[cfg(all(test,
feature
=
"test-env"
))]
mod
tests
{
use
super
::
*
;
use
crate
::
storage
::
Key
;
use
crate
::{
storage
::{
Key
,
alloc
::
ForwardAlloc
,
},
test_utils
::
run_test
,
env
::
TestEnv
,
};
fn
dummy_cell
()
->
SyncCell
<
i32
>
{
unsafe
{
let
mut
alloc
=
crate
::
storage
::
alloc
::
ForwardAlloc
::
from_raw_parts
(
let
mut
alloc
=
ForwardAlloc
::
from_raw_parts
(
Key
([
0x0
;
32
])
);
SyncCell
::
new_using_alloc
(
&
mut
alloc
)
...
...
@@ -465,28 +466,98 @@ mod tests {
}
#[test]
fn
count_reads
()
{
run_test
(||
{
let
cell
=
dummy_cell
();
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
fn
count_rw_get
()
{
// Repetitions performed.
const
N
:
u32
=
5
;
let
mut
cell
=
dummy_cell
();
// Asserts initial reads and writes are zero.
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
// Repeated reads on the same cell.
for
_i
in
0
..
N
{
cell
.get
();
assert_eq!
(
TestEnv
::
total_reads
(),
1
);
cell
.get
();
cell
.get
();
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
}
// Flush the cell and assert reads and writes.
cell
.flush
();
assert_eq!
(
TestEnv
::
total_reads
(),
1
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
}
#[test]
fn
count_rw_get_mut
()
{
// Repetitions performed.
const
N
:
u32
=
5
;
let
mut
cell
=
dummy_cell
();
// Asserts initial reads and writes are zero.
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
// Repeated mutable reads on the same cell.
for
_i
in
0
..
N
{
cell
.get_mut
();
assert_eq!
(
TestEnv
::
total_reads
(),
1
);
})
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
}
// Flush the cell and assert reads and writes.
cell
.flush
();
assert_eq!
(
TestEnv
::
total_reads
(),
1
);
assert_eq!
(
TestEnv
::
total_writes
(),
1
);
}
#[test]
fn
count_writes
()
{
run_test
(||
{
let
mut
cell
=
dummy_cell
();
fn
count_rw_set
()
{
// Repetitions performed.
const
N
:
u32
=
5
;
let
mut
cell
=
dummy_cell
();
// Asserts initial reads and writes are zero.
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
// Repeated writes to the same cell.
for
_i
in
0
..
N
{
cell
.set
(
42
);
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
cell
.set
(
1
);
assert_eq!
(
TestEnv
::
total_writes
(),
1
);
cell
.set
(
2
);
cell
.set
(
3
);
assert_eq!
(
TestEnv
::
total_writes
(),
3
);
})
}
// Flush the cell and assert reads and writes.
cell
.flush
();
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
1
);
}
#[test]
fn
count_rw_clear
()
{
// Repetitions performed.
const
N
:
u32
=
5
;
let
mut
cell
=
dummy_cell
();
// Asserts initial reads and writes are zero.
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
// Repeated writes to the same cell.
for
_i
in
0
..
N
{
cell
.clear
();
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
0
);
}
// Flush the cell and assert reads and writes.
cell
.flush
();
assert_eq!
(
TestEnv
::
total_reads
(),
0
);
assert_eq!
(
TestEnv
::
total_writes
(),
1
);
}
}
pdsl_core/src/storage/chunk/mod.rs
View file @
be555eb9
...
...
@@ -22,7 +22,6 @@ mod sync_chunk;
pub
(
crate
)
use
self
::{
raw_chunk
::
RawChunkCell
,
typed_chunk
::
TypedChunkCell
,
};
pub
use
self
::{
...
...
pdsl_core/src/storage/chunk/sync_chunk.rs
deleted
100644 → 0
View file @
db20a6cf
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of pDSL.
//
// pDSL is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// pDSL is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with pDSL. If not, see <http://www.gnu.org/licenses/>.
use
crate
::{
storage
::{
Key
,
chunk
::{
TypedChunk
,
TypedChunkCell
,
},
Allocator
,
},
memory
::
collections
::
btree_map
::{
BTreeMap
,
Entry
,
},
};
use
core
::
cell
::
RefCell
;
/// A chunk of synchronized cells.
///
/// Provides mutable and read-optimized access to the associated constract storage slot.
///
/// # Guarantees
///
/// - `Owned`
/// - `Typed`
/// - `Opt. Reads`
/// - `Mutable`
///
/// Read more about kinds of guarantees and their effect [here](../index.html#guarantees).
#[derive(Debug)]
pub
struct
SyncChunk
<
T
>
{
/// The underlying chunk of cells.
chunk
:
TypedChunk
<
T
>
,
/// The cached element.
elems
:
Cache
<
T
>
,
}
/// A single cache entry for a copy chunk cell.
#[cfg(not(feature
=
"std"
))]
type
CacheEntry
<
'a
,
T
>
=
Entry
<
'a
,
u32
,
Option
<
T
>>
;
/// A single cache entry for a copy chunk cell.
#[cfg(feature
=
"std"
)]
type
CacheEntry
<
'a
,
T
>
=
Entry
<
'a
,
u32
,
Option
<
T
>>
;
/// A single cell within a chunk of copy cells.
#[derive(Debug)]
pub
struct
SyncChunkCell
<
'a
,
T
>
{
/// The underlying cell within the chunk of cells.
cell
:
TypedChunkCell
<
'a
,
T
>
,
/// The cached entry for the cell.
elem
:
CacheEntry
<
'a
,
T
>
,
}
impl
<
'a
,
T
>
SyncChunkCell
<
'a
,
T
>
{
/// Creates a new cell within a chunk of copy cells.
///
/// # Safety
///
/// This is unsafe since it doesn't check aliasing of cells
/// or if the cell and the cache entry are actually associated
/// with each other.
pub
(
self
)
unsafe
fn
new_unchecked
(
cell
:
TypedChunkCell
<
'a
,
T
>
,
elem
:
CacheEntry
<
'a
,
T
>
)
->
Self
{
Self
{
cell
,
elem
}
}
/// Removes the value stored in this cell.
pub
fn
clear
(
self
)
{
let
mut
this
=
self
;
match
this
.elem
{
Entry
::
Occupied
(
mut
occupied
)
=>
{
this
.cell
.clear
();
occupied
.insert
(
None
);
}
Entry
::
Vacant
(
vacant
)
=>
{
this
.cell
.clear
();
vacant
.insert
(
None
);
}
}
}
}
impl
<
'a
,
T
>
SyncChunkCell
<
'a
,
T
>
where
T
:
parity_codec
::
Decode
{
/// Removes the value from the cell and returns the removed value.
///
/// # Note
///
/// Prefer using `clear` if you are not interested in the return value.
#[must_use]
pub
fn
remove
(
self
)
->
Option
<
T
>
{
let
mut
this
=
self
;
match
this
.elem
{
Entry
::
Occupied
(
mut
occupied
)
=>
{
this
.cell
.clear
();
occupied
.insert
(
None
)
}
Entry
::
Vacant
(
vacant
)
=>
{
let
old
=
this
.cell
.load
();
this
.cell
.clear
();
vacant
.insert
(
None
);
old
}
}
}
}
impl
<
'a
,
T
>
SyncChunkCell
<
'a
,
T
>
where
T
:
parity_codec
::
Encode
{
/// Stores the new value into the cell.
pub
fn
set
(
self
,
val
:
T
)
{
let
mut
this
=
self
;
match
this
.elem
{
Entry
::
Occupied
(
mut
occupied
)
=>
{
this
.cell
.store
(
&
val
);
occupied
.insert
(
Some
(
val
));
}
Entry
::
Vacant
(
vacant
)
=>
{
this
.cell
.store
(
&
val
);
vacant
.insert
(
Some
(
val
));
}
}
}
}
impl
<
'a
,
T
>
SyncChunkCell
<
'a
,
T
>
where
T
:
parity_codec
::
Codec
{
/// Mutates the value of this cell.
///
/// Returns an immutable reference to the result if
/// a mutation happened, otherwise `None` is returned.
///
/// # Note
///
/// Prefer using `set` if you are not interested in the return value.
pub
fn
mutate_with
<
F
>
(
self
,
f
:
F
)
->
Option
<&
'a
T
>
where
F
:
FnOnce
(
&
mut
T
)
{
let
mut
this
=
self
;
match
this
.elem
{
Entry
::
Occupied
(
occupied
)
=>
{
if
let
Some
(
elem
)
=
occupied
.into_mut
()
{
f
(
elem
);
this
.cell
.store
(
elem
);
return
Some
(
&*
elem
)
}
None
}
Entry
::
Vacant
(
vacant
)
=>
{
let
mut
ret
=
false
;
let
mut
elem
=
this
.cell
.load
();
if
let
Some
(
elem
)
=
&
mut
elem
{
f
(
elem
);
this
.cell
.store
(
&*
elem
);
ret
=
true
;
}
let
res
=
(
&*
vacant
.insert
(
elem
))
.into
();
if
ret
{
return
res
}
None
}
}
}
/// Replaces the value of this cell and returns its previous value.
///
/// # Note
///
/// Prefer using `set` if you are not interested in the return value.
#[must_use]
pub
fn
replace
(
self
,
val
:
T
)
->
Option
<
T
>
{
let
mut
this
=
self
;
match
this
.elem
{
Entry
::
Occupied
(
mut
occupied
)
=>
{
this
.cell
.store
(
&
val
);
occupied
.insert
(
Some
(
val
))
}
Entry
::
Vacant
(
vacant
)
=>
{
let
old
=
this
.cell
.load
();
this
.cell
.store
(
&
val
);
vacant
.insert
(
Some
(
val
));
old
}
}
}
}
/// Stores the values of synchronized cells.
///
/// # Note
///
/// An element counts as synchronized if its version in the contract
/// storage and the version in the cache are identical.
#[derive(Debug,
PartialEq,
Eq)]
struct
Cache
<
T
>
{
/// The synchronized values of associated cells.
elems
:
RefCell
<
BTreeMap
<
u32
,
Option
<
T
>>>
,
}
impl
<
T
>
Default
for
Cache
<
T
>
{
fn
default
()
->
Self
{
Self
{
elems
:
RefCell
::
new
(
BTreeMap
::
new
())
}
}
}
/// A cached entity.
///
/// This is either in sync with the contract storage or out of sync.
#[derive(Debug,
Copy,
Clone,
PartialEq,
Eq)]
pub
enum
Cached
<
T
>
{
Desync
,
Sync
(
Option
<
T
>
),
}
impl
<
T
>
Cache
<
T
>
{
/// Inserts or updates a value associated with the `n`-th cell.
///
/// Returns an immutable reference to the new value.
pub
fn
upsert
(
&
self
,
n
:
u32
,
val
:
Option
<
T
>
)
->
Option
<&
T
>
{
let
elems
:
&
mut
BTreeMap
<
u32
,
Option
<
T
>>
=
unsafe
{
&
mut
*
self
.elems
.as_ptr
()
};
match
elems
.entry
(
n
)
{
Entry
::
Occupied
(
mut
occupied
)
=>
{
occupied
.insert
(
val
);
(
&*
occupied
.into_mut
())
.into
()
}
Entry
::
Vacant
(
vacant
)
=>
{
(
&*
vacant
.insert
(
val
))
.into
()
}
}
}
/// Returns the synchronized value of the `n`-th cell if any.
pub
fn
get
(
&
self
,
n
:
u32
)
->
Cached
<&
T
>
{
let
elems
:
&
mut
BTreeMap
<
u32
,
Option
<
T
>>
=
unsafe
{
&
mut
*
self
.elems
.as_ptr
()
};
match
elems
.get
(
&
n
)
{
Some
(
opt_elem
)
=>
Cached
::
Sync
(
opt_elem
.into
()),
None
=>
Cached
::
Desync
,
}
}
/// Returns the cache entry for the `n`-th cell.
pub
fn
entry
(
&
mut
self
,
n
:
u32
)
->
CacheEntry
<
T
>
{
self
.elems
.get_mut
()
.entry
(
n
)
}
}
impl
<
T
>
parity_codec
::
Encode
for
SyncChunk
<
T
>
{
fn
encode_to
<
W
:
parity_codec
::
Output
>
(
&
self
,
dest
:
&
mut
W
)
{
self
.chunk
.encode_to
(
dest
)
}
}
impl
<
T
>
parity_codec
::
Decode
for
SyncChunk
<
T
>
{
fn
decode
<
I
:
parity_codec
::
Input
>
(
input
:
&
mut
I
)
->
Option
<
Self
>
{
TypedChunk
::
decode
(
input
)
.map
(|
typed_chunk
|
Self
{
chunk
:
typed_chunk
,
elems
:
Cache
::
default
(),
})
}
}
impl
<
T
>
SyncChunk
<
T
>
{
/// Allocates a new sync cell chunk using the given storage allocator.
///
/// # Safety
///
/// The is unsafe because it does not check if the associated storage
/// does not alias with storage allocated by other storage allocators.
pub
unsafe
fn
new_using_alloc
<
A
>
(
alloc
:
&
mut
A
)
->
Self
where
A
:
Allocator
{
Self
{
chunk
:
TypedChunk
::
new_using_alloc
(
alloc
),
elems
:
Cache
::
default
(),
}
}
/// Returns the unterlying key to the cells.
///
/// # Note
///
/// This is a low-level utility getter and should
/// normally not be required by users.
pub
fn
cells_key
(
&
self
)
->
Key
{
self
.chunk
.cells_key
()
}
/// Returns an accessor to the `n`-th cell.
fn
cell_at
(
&
mut
self
,
n
:
u32
)
->
SyncChunkCell
<
T
>
{
unsafe
{
SyncChunkCell
::
new_unchecked
(
self
.chunk
.cell_at
(
n
),
self
.elems
.entry
(
n
)
)
}
}
/// Clear the `n`-th cell.
///
/// # Errors
///
/// If `n` is out of bounds.
pub
fn
clear
(
&
mut
self
,
n
:
u32
)
{
self
.cell_at
(
n
)
.clear
()
}
}
impl
<
T
>
SyncChunk
<
T
>
where
T
:
parity_codec
::
Decode
{
/// Returns the value of the `n`-th cell if any.
///
/// # Errors
///
/// If `n` is out of bounds.
pub
fn
get
(
&
self
,
n
:
u32
)
->
Option
<&
T
>
{
if
let
Cached
::
Sync
(
cached
)
=
self
.elems
.get
(
n
)
{
return
cached
}
self
.load
(
n
)
}
/// Returns the value of the `n`-th cell if any.
///
/// # Note
///
/// Prefer using [`get`](struct.SyncChunk.html#method.get)
/// to avoid unnecesary contract storage accesses.
///
/// # Errors
///
/// If `n` is out of bounds.
fn
load
(
&
self
,
n
:
u32
)
->
Option
<&
T
>
{
self
.elems
.upsert
(
n
,
self
.chunk
.load
(
n
)
)
}
/// Clears the `n`-th cell and returns its previous value if any.
///
/// # Note
///
/// Use [`clear`](struct.SyncChunk.html#method.clear) instead
/// if you are not interested in the old return value.
///
/// # Errors
///
/// If `n` is out of bounds.
#[must_use]
pub
fn
remove
(
&
mut
self
,
n
:
u32
)
->
Option
<
T
>
{
self
.cell_at
(
n
)
.remove
()
}
}
impl
<
T
>
SyncChunk
<
T
>
where
T
:
parity_codec
::
Encode
{
/// Sets the value of the `n`-th cell.
///
/// # Errors
///
/// If `n` is out of bounds.
pub
fn
set
(
&
mut
self
,
n
:
u32
,
val
:
T
)
{
self
.cell_at
(
n
)
.set
(
val
)
}
}
impl
<
T
>
SyncChunk
<
T
>
where
T
:
parity_codec
::
Codec
{
/// Sets the value of the `n`-th cell and returns its old value if any.
///
/// # Note
///
/// Use [`set`](struct.SyncChunk.html#method.set) instead
/// if you are not interested in the old return value.
///
/// # Errors
///
/// If `n` is out of bounds.
#[must_use]
pub
fn
replace
(
&
mut
self
,
n
:
u32
,
val
:
T
)
->
Option
<
T
>
{
self
.cell_at
(
n
)
.replace
(
val
)
}
/// Mutates the value of the `n`-th cell if any.
///
/// Returns an immutable reference to the result if
/// a mutation happened, otherwise `None` is returned.
///
/// # Errors
///
/// If `n` is out of bounds.
pub
fn
mutate_with
<
F
>
(
&
mut
self
,
n
:
u32
,
f
:
F
)
->
Option
<&
T
>
where
F
:
FnOnce
(
&
mut
T
)
{
self
.cell_at
(
n
)
.mutate_with
(
f
)
}
}
#[cfg(all(test,
feature
=
"test-env"
))]
mod
tests
{
use
super
::
*
;
use
crate
::{
test_utils
::
run_test
,
env
::
TestEnv
,
};
fn
dummy_chunk
()
->
SyncChunk
<
u32
>
{
unsafe
{
let
mut
alloc
=
crate
::
storage
::
alloc
::
ForwardAlloc
::
from_raw_parts
(
Key
([
0x0
;
32
])
);
SyncChunk
::
new_using_alloc
(
&
mut
alloc
)