/src/libtorrent/src/packet_buffer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2010-2012, 2014, 2016-2017, 2019-2020, Arvid Norberg |
4 | | Copyright (c) 2017, Alden Torres |
5 | | All rights reserved. |
6 | | |
7 | | Redistribution and use in source and binary forms, with or without |
8 | | modification, are permitted provided that the following conditions |
9 | | are met: |
10 | | |
11 | | * Redistributions of source code must retain the above copyright |
12 | | notice, this list of conditions and the following disclaimer. |
13 | | * Redistributions in binary form must reproduce the above copyright |
14 | | notice, this list of conditions and the following disclaimer in |
15 | | the documentation and/or other materials provided with the distribution. |
16 | | * Neither the name of the author nor the names of its |
17 | | contributors may be used to endorse or promote products derived |
18 | | from this software without specific prior written permission. |
19 | | |
20 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
21 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
24 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | | POSSIBILITY OF SUCH DAMAGE. |
31 | | |
32 | | */ |
33 | | |
34 | | #include "libtorrent/aux_/packet_buffer.hpp" |
35 | | #include "libtorrent/assert.hpp" |
36 | | #include "libtorrent/aux_/invariant_check.hpp" |
37 | | |
38 | | namespace libtorrent { |
39 | | namespace aux { |
40 | | |
41 | | bool compare_less_wrap(std::uint32_t lhs, std::uint32_t rhs |
42 | | , std::uint32_t mask); |
43 | | |
44 | | #if TORRENT_USE_INVARIANT_CHECKS |
45 | | void packet_buffer::check_invariant() const |
46 | | { |
47 | | int count = 0; |
48 | | for (index_type i = 0; i < m_capacity; ++i) |
49 | | { |
50 | | count += m_storage[i] ? 1 : 0; |
51 | | } |
52 | | TORRENT_ASSERT(count == m_size); |
53 | | } |
54 | | #endif |
55 | | |
56 | | packet_ptr packet_buffer::insert(index_type idx, packet_ptr value) |
57 | 0 | { |
58 | 0 | INVARIANT_CHECK; |
59 | |
|
60 | 0 | TORRENT_ASSERT_VAL(idx <= 0xffff, idx); |
61 | | // you're not allowed to insert NULLs! |
62 | 0 | TORRENT_ASSERT(value); |
63 | |
|
64 | 0 | if (!value) return remove(idx); |
65 | | |
66 | 0 | if (m_size != 0) |
67 | 0 | { |
68 | 0 | if (compare_less_wrap(idx, m_first, 0xffff)) |
69 | 0 | { |
70 | | // Index comes before m_first. If we have room, we can simply |
71 | | // adjust m_first backward. |
72 | |
|
73 | 0 | std::uint32_t free_space = 0; |
74 | |
|
75 | 0 | for (index_type i = (m_first - 1) & (m_capacity - 1); |
76 | 0 | i != (m_first & (m_capacity - 1)); i = (i - 1) & (m_capacity - 1)) |
77 | 0 | { |
78 | 0 | if (m_storage[i & (m_capacity - 1)]) |
79 | 0 | break; |
80 | 0 | ++free_space; |
81 | 0 | } |
82 | |
|
83 | 0 | if (((m_first - idx) & 0xffff) > free_space) |
84 | 0 | reserve(((m_first - idx) & 0xffff) + m_capacity - free_space); |
85 | |
|
86 | 0 | m_first = idx; |
87 | 0 | } |
88 | 0 | else if (idx >= m_first + m_capacity) |
89 | 0 | { |
90 | 0 | reserve(idx - m_first + 1); |
91 | 0 | } |
92 | 0 | else if (idx < m_first) |
93 | 0 | { |
94 | | // We have wrapped. |
95 | 0 | if (idx >= ((m_first + m_capacity) & 0xffff) && m_capacity < 0xffff) |
96 | 0 | { |
97 | 0 | reserve(m_capacity + (idx + 1 - ((m_first + m_capacity) & 0xffff))); |
98 | 0 | } |
99 | 0 | } |
100 | 0 | if (compare_less_wrap(m_last, (idx + 1) & 0xffff, 0xffff)) |
101 | 0 | m_last = (idx + 1) & 0xffff; |
102 | 0 | } |
103 | 0 | else |
104 | 0 | { |
105 | 0 | m_first = idx; |
106 | 0 | m_last = (idx + 1) & 0xffff; |
107 | 0 | } |
108 | |
|
109 | 0 | if (m_capacity == 0) reserve(16); |
110 | |
|
111 | 0 | packet_ptr old_value = std::move(m_storage[idx & (m_capacity - 1)]); |
112 | 0 | m_storage[idx & (m_capacity - 1)] = std::move(value); |
113 | |
|
114 | 0 | if (m_size == 0) m_first = idx; |
115 | | // if we're just replacing an old value, the number |
116 | | // of elements in the buffer doesn't actually increase |
117 | 0 | if (!old_value) ++m_size; |
118 | |
|
119 | 0 | TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); |
120 | 0 | return old_value; |
121 | 0 | } |
122 | | |
123 | | packet* packet_buffer::at(index_type idx) const |
124 | 0 | { |
125 | 0 | INVARIANT_CHECK; |
126 | 0 | if (idx >= m_first + m_capacity) |
127 | 0 | return nullptr; |
128 | | |
129 | 0 | if (compare_less_wrap(idx, m_first, 0xffff)) |
130 | 0 | return nullptr; |
131 | | |
132 | 0 | std::size_t const mask = m_capacity - 1; |
133 | 0 | return m_storage[idx & mask].get(); |
134 | 0 | } |
135 | | |
136 | | void packet_buffer::reserve(std::uint32_t size) |
137 | 0 | { |
138 | 0 | INVARIANT_CHECK; |
139 | 0 | TORRENT_ASSERT_VAL(size <= 0xffff, size); |
140 | 0 | std::uint32_t new_size = m_capacity == 0 ? 16 : m_capacity; |
141 | |
|
142 | 0 | while (new_size < size) |
143 | 0 | new_size <<= 1; |
144 | |
|
145 | 0 | aux::unique_ptr<packet_ptr[], index_type> new_storage(new packet_ptr[new_size]); |
146 | |
|
147 | 0 | for (index_type i = m_first; i < (m_first + m_capacity); ++i) |
148 | 0 | new_storage[i & (new_size - 1)] = std::move(m_storage[i & (m_capacity - 1)]); |
149 | |
|
150 | 0 | m_storage = std::move(new_storage); |
151 | 0 | m_capacity = new_size; |
152 | 0 | } |
153 | | |
154 | | packet_ptr packet_buffer::remove(index_type idx) |
155 | 0 | { |
156 | 0 | INVARIANT_CHECK; |
157 | | // TODO: use compare_less_wrap for this comparison as well |
158 | 0 | if (idx >= m_first + m_capacity) |
159 | 0 | return packet_ptr(); |
160 | | |
161 | 0 | if (compare_less_wrap(idx, m_first, 0xffff)) |
162 | 0 | return packet_ptr(); |
163 | | |
164 | 0 | std::size_t const mask = m_capacity - 1; |
165 | 0 | packet_ptr old_value = std::move(m_storage[idx & mask]); |
166 | 0 | m_storage[idx & mask].reset(); |
167 | |
|
168 | 0 | if (old_value) |
169 | 0 | { |
170 | 0 | --m_size; |
171 | 0 | if (m_size == 0) m_last = m_first; |
172 | 0 | } |
173 | |
|
174 | 0 | if (idx == m_first && m_size != 0) |
175 | 0 | { |
176 | 0 | ++m_first; |
177 | 0 | for (index_type i = 0; i < m_capacity; ++i, ++m_first) |
178 | 0 | if (m_storage[m_first & mask]) break; |
179 | 0 | m_first &= 0xffff; |
180 | 0 | } |
181 | |
|
182 | 0 | if (((idx + 1) & 0xffff) == m_last && m_size != 0) |
183 | 0 | { |
184 | 0 | --m_last; |
185 | 0 | for (index_type i = 0; i < m_capacity; ++i, --m_last) |
186 | 0 | if (m_storage[m_last & mask]) break; |
187 | 0 | ++m_last; |
188 | 0 | m_last &= 0xffff; |
189 | 0 | } |
190 | |
|
191 | 0 | TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first); |
192 | 0 | return old_value; |
193 | 0 | } |
194 | | } |
195 | | } |