Alioth Code Coverage

addressable.rs87.65%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::ops::RangeBounds;
16
17use crate::mem::{Result, error};
18
19pub trait SlotBackend {
20 fn size(&self) -> u64;
21}
22
23#[derive(Debug)]
24struct Slot<B>
25where
26 B: SlotBackend,
27{
28 addr: u64,
29 backend: B,
30}
31
32impl<B> Slot<B>
33where
34 B: SlotBackend,
35{
36 fn new(addr: u64, backend: B) -> Result<Self> {624x
37 if backend.size() == 0 {624x
38 return error::ZeroSizedSlot.fail();3x
39 }621x
40 match (backend.size() - 1).checked_add(addr) {621x
41 None => error::ExceedsLimit {3x
42 addr,3x
43 size: backend.size(),3x
44 }3x
45 .fail(),3x
46 Some(_) => Ok(Self { addr, backend }),618x
47 }
48 }624x
49
50 fn max_addr(&self) -> u64 {2481x
51 (self.backend.size() - 1) + self.addr2481x
52 }2481x
53}
54
55#[derive(Debug)]
56pub struct Addressable<B>
57where
58 B: SlotBackend,
59{
60 slots: Vec<Slot<B>>,
61}
62
63impl<B> Default for Addressable<B>
64where
65 B: SlotBackend,
66{
67 fn default() -> Self {283x
68 Addressable { slots: Vec::new() }283x
69 }283x
70}
71
72impl<B> Addressable<B>
73where
74 B: SlotBackend,
75{
76 pub fn new() -> Self {202x
77 Self::default()202x
78 }202x
79
80 pub fn iter(&self) -> impl DoubleEndedIterator<Item = (u64, &B)> {27x
81 self.slots.iter().map(|slot| (slot.addr, &slot.backend))39x
82 }27x
83
84 pub fn drain(12x
85 &mut self,12x
86 range: impl RangeBounds<usize>,12x
87 ) -> impl DoubleEndedIterator<Item = (u64, B)> + '_ {12x
88 self.slots.drain(range).map(|s| (s.addr, s.backend))12x
89 }12x
90
91 pub fn is_empty(&self) -> bool {92x
92 self.slots.is_empty()92x
93 }92x
94
95 pub fn last(&self) -> Option<(u64, &B)> {3x
96 self.slots.last().map(|slot| (slot.addr, &slot.backend))3x
97 }3x
98}
99
100impl<B> Addressable<B>
101where
102 B: SlotBackend,
103{
104 pub fn add(&mut self, addr: u64, backend: B) -> Result<&mut B> {615x
105 let slot = Slot::new(addr, backend)?;615x
106 let result = match self.slots.binary_search_by_key(&addr, |s| s.addr) {615x
107 Ok(index) => Err(&self.slots[index]),3x
108 Err(index) => {612x
109 if index < self.slots.len() && self.slots[index].addr <= slot.max_addr() {612x
110 Err(&self.slots[index])9x
111 } else if index > 0 && slot.addr <= self.slots[index - 1].max_addr() {603x
112 Err(&self.slots[index - 1])9x
113 } else {
114 Ok(index)594x
115 }
116 }
117 };
118 match result {615x
119 Err(curr_slot) => error::Overlap {21x
120 new_item: [slot.addr, slot.max_addr()],21x
121 exist_item: [curr_slot.addr, curr_slot.max_addr()],21x
122 }21x
123 .fail(),21x
124 Ok(index) => {594x
125 self.slots.insert(index, slot);594x
126 // TODO add some compiler hint to eliminate bound check?
127 Ok(&mut self.slots[index].backend)594x
128 }
129 }
130 }615x
131
132 pub fn remove(&mut self, addr: u64) -> Result<B> {51x
133 match self.slots.binary_search_by_key(&addr, |s| s.addr) {51x
134 Ok(index) => Ok(self.slots.remove(index).backend),48x
135 Err(_) => error::NotMapped { addr }.fail(),3x
136 }
137 }51x
138
139 pub fn search(&self, addr: u64) -> Option<(u64, &B)> {2757x
140 match self.slots.binary_search_by_key(&addr, |s| s.addr) {2757x
141 Ok(index) => Some((self.slots[index].addr, &self.slots[index].backend)),750x
142 Err(0) => None,3x
143 Err(index) => {2004x
144 let candidate = &self.slots[index - 1];2004x
145 if addr <= candidate.max_addr() {2004x
146 Some((candidate.addr, &candidate.backend))1803x
147 } else {
148 None201x
149 }
150 }
151 }
152 }2757x
153
154 pub fn search_next(&self, addr: u64) -> Option<(u64, &B)> {
155 match self.slots.binary_search_by_key(&addr, |s| s.addr) {
156 Ok(index) => Some((self.slots[index].addr, &self.slots[index].backend)),
157 Err(0) => None,
158 Err(index) => {
159 let candidate = &self.slots[index - 1];
160 if addr <= candidate.max_addr() {
161 Some((candidate.addr, &candidate.backend))
162 } else {
163 self.slots.get(index).map(|slot| (slot.addr, &slot.backend))
164 }
165 }
166 }
167 }
168}
169
170#[cfg(test)]
171#[path = "addressable_test.rs"]
172mod tests;
173