• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 4637370737

pending completion
4637370737

push

github

GitHub
Merge pull request #1353 from evoskuil/master

102 of 102 new or added lines in 5 files covered. (100.0%)

9438 of 11391 relevant lines covered (82.85%)

6703526.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.87
/include/bitcoin/system/impl/machine/stack.ipp
1
/**
2
 * Copyright (c) 2011-2022 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#ifndef LIBBITCOIN_SYSTEM_MACHINE_STACK_IPP
20
#define LIBBITCOIN_SYSTEM_MACHINE_STACK_IPP
21

22
#include <iterator>
23
#include <list>
24
#include <type_traits>
25
#include <utility>
26
#include <variant>
27
#include <vector>
28
#include <bitcoin/system/data/data.hpp>
29
#include <bitcoin/system/define.hpp>
30
#include <bitcoin/system/machine/number.hpp>
31
#include <bitcoin/system/math/math.hpp>
32

33
namespace libbitcoin {
34
namespace system {
35
namespace machine {
36

37
template <typename Container>
38
INLINE stack<Container>::stack() NOEXCEPT
1,555✔
39
  : container_{}, tether_{}
1,555✔
40
{
41
}
42

43
template <typename Container>
44
INLINE stack<Container>::stack(Container&& container) NOEXCEPT
23✔
45
  : container_(std::move(container)), tether_{}
23✔
46
{
47
}
48

49
// Pure stack abstraction.
50
// ----------------------------------------------------------------------------
51

52
template <typename Container>
53
INLINE const stack_variant& stack<Container>::top() const NOEXCEPT
12,058✔
54
{
55
    BC_ASSERT(!empty());
56
    return container_.back();
3,613✔
57
}
58

59
template <typename Container>
60
INLINE stack_variant stack<Container>::pop() NOEXCEPT
1,130✔
61
{
62
    BC_ASSERT(!empty());
63
    stack_variant temporary{ std::move(container_.back()) };
1,130✔
64
    container_.pop_back();
1,130✔
65
    return temporary;
1,130✔
66
}
67

68
template <typename Container>
69
INLINE void stack<Container>::drop() NOEXCEPT
10,182✔
70
{
71
    BC_ASSERT(!empty());
72
    container_.pop_back();
745✔
73
}
74

75
template <typename Container>
76
INLINE bool stack<Container>::empty() const NOEXCEPT
8,283✔
77
{
78
    return container_.empty();
8,283✔
79
}
80

81
template <typename Container>
82
INLINE size_t stack<Container>::size() const NOEXCEPT
33,394✔
83
{
84
    return container_.size();
254✔
85
}
86

87
template <typename Container>
88
INLINE void stack<Container>::push(data_chunk&& value) NOEXCEPT
167✔
89
{
90
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
91
    container_.push_back(make_external(std::move(value), tether_));
167✔
92
    BC_POP_WARNING()
93
}
94

95
template <typename Container>
96
INLINE void stack<Container>::push(stack_variant&& value) NOEXCEPT
97
{
98
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
99
    container_.push_back(std::move(value));
100
    BC_POP_WARNING()
101
}
102

103
template <typename Container>
104
INLINE void stack<Container>::push(const stack_variant& value) NOEXCEPT
9,957✔
105
{
106
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
107
    container_.push_back(value);
9,957✔
108
    BC_POP_WARNING()
109
}
110

111
template <typename Container>
112
INLINE void stack<Container>::emplace_boolean(bool value) NOEXCEPT
2,014✔
113
{
114
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
115
    container_.emplace_back(value);
2,014✔
116
    BC_POP_WARNING()
117
}
118

119
template <typename Container>
120
INLINE void stack<Container>::emplace_integer(int64_t value) NOEXCEPT
7,246✔
121
{
122
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
123
    container_.emplace_back(value);
7,246✔
124
    BC_POP_WARNING()
125
}
126

127
template <typename Container>
128
INLINE void stack<Container>::emplace_chunk(const chunk_xptr& value) NOEXCEPT
4,470✔
129
{
130
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
131
    container_.emplace_back(value.get());
4,470✔
132
    BC_POP_WARNING()
133
}
134

135
// Positional (stack cheats).
136
// ----------------------------------------------------------------------------
137
// These optimizations prevent used of std::stack.
138

139
template <typename Container>
140
INLINE void stack<Container>::erase(size_t index) NOEXCEPT
16✔
141
{
142
    BC_ASSERT(index < size());
143
    container_.erase(std::prev(container_.end(), add1(index)));
16✔
144
}
145

146
template <typename Container>
147
INLINE void stack<Container>::swap(size_t left_index,
148✔
148
    size_t right_index) NOEXCEPT
149
{
150
    BC_ASSERT(left_index < size() && right_index < size());
151

152
    if constexpr (vector_)
153
    {
154
        const auto back = sub1(size());
155
        std::swap(
174✔
156
            container_[back - left_index],
174✔
157
            container_[back - right_index]);
174✔
158
    }
159

160
    if constexpr (linked_)
161
    {
162
        const auto end = container_.end();
×
163
        std::swap(
×
164
            *std::prev(end, add1(left_index)),
×
165
            *std::prev(end, add1(right_index)));
×
166
    }
167
}
168

169
template <typename Container>
170
INLINE const stack_variant& stack<Container>::peek(size_t index) const NOEXCEPT
6,616✔
171
{
172
    BC_ASSERT(index < size());
173

174
    if constexpr (vector_)
175
        return container_[sub1(size()) - index];
9,916✔
176

177
    if constexpr (linked_)
178
        return *std::prev(container_.end(), add1(index));
×
179
}
180

181
// Aliases.
182
// ----------------------------------------------------------------------------
183

184
template <typename Container>
185
INLINE bool stack<Container>::peek_signed4(int32_t& value) const NOEXCEPT
4,738✔
186
{
187
    return peek_signed<4>(value);
4,738✔
188
}
189

190
template <typename Container>
191
INLINE bool stack<Container>::peek_signed5(int64_t& value) const NOEXCEPT
12✔
192
{
193
    return peek_signed<5>(value);
12✔
194
}
195

196
/// Variant data conversions.
197
// ----------------------------------------------------------------------------
198

199
// Generalized integer peek for varying bit widths up to 8 bytes.
200
// Chunk to integer conversions are constrained by caller (4 or 5 bytes).
201
template <typename Container>
202
template<size_t Bytes, typename Integer,
203
    if_not_lesser<sizeof(Integer), Bytes>,
204
    if_signed_integral_integer<Integer>>
205
inline bool stack<Container>::peek_signed(Integer& value) const NOEXCEPT
4,750✔
206
{
207
    using namespace number;
208
    auto result{ true };
4,750✔
209

210
    std::visit(overload
4,750✔
211
    {
212
        [&](bool vary) NOEXCEPT
40✔
213
        {
214
            // This is never executed in standard scripts.
215
            value = boolean::to_integer<Bytes>(vary);
40✔
216
        },
217
        [&](int64_t vary) NOEXCEPT
4,419✔
218
        {
219
            // This is the canonical use case (bounds check only).
220
            result = integer<Bytes>::from_integer(value, vary);
4,419✔
221
        },
222
        [&](const chunk_xptr& vary) NOEXCEPT
291✔
223
        {
224
            // This is never executed in standard scripts.
225
            result = integer<Bytes>::from_chunk(value , *vary);
291✔
226
        }
227
    }, top());
228

229
    return result;
4,750✔
230
}
231

232
template <typename Container>
233
inline bool stack<Container>::peek_bool() const NOEXCEPT
1,589✔
234
{
235
    using namespace number;
236
    bool value{};
1,589✔
237

238
    std::visit(overload
1,589✔
239
    {
240
        [&](bool vary) NOEXCEPT
711✔
241
        {
242
            // This is the canonical use case.
243
            value = vary;
711✔
244
        },
245
        [&](int64_t vary) NOEXCEPT
828✔
246
        {
247
            // This is never executed in standard scripts.
248
            value = boolean::to_bool(vary);
828✔
249
        },
250
        [&](const chunk_xptr& vary) NOEXCEPT
50✔
251
        {
252
            // This is never executed in standard scripts.
253
            value = boolean::from_chunk(*vary);
50✔
254
        }
255
    }, top());
256

257
    return value;
1,589✔
258
}
259

260
// This differs from peek_bool in that a stack chunk must be empty.
261
template <typename Container>
262
inline bool stack<Container>::peek_strict_bool() const NOEXCEPT
2,050✔
263
{
264
    using namespace number;
265
    bool value{};
2,050✔
266

267
    std::visit(overload
2,050✔
268
    {
269
        [&](bool vary) NOEXCEPT
1,200✔
270
        {
271
            // This is the canonical use case (after bip147).
272
            value = vary;
1,200✔
273
        },
274
        [&](int64_t vary) NOEXCEPT
848✔
275
        {
276
            // This may be executed in standard scripts (before bip147).
277
            value = boolean::to_bool(vary);
848✔
278
        },
279
        [&](const chunk_xptr& vary) NOEXCEPT
2✔
280
        {
281
            // This may be executed in standard scripts (before bip147).
282
            value = boolean::strict_from_chunk(*vary);
2✔
283
        }
284
    }, top());
285

286
    return value;
2,050✔
287
}
288

289
// This is the only source of peek/pop (read) tethering.
290
template <typename Container>
291
inline chunk_xptr stack<Container>::peek_chunk() const NOEXCEPT
3,578✔
292
{
293
    using namespace number;
294
    chunk_xptr value{};
295

296
    std::visit(overload
3,578✔
297
    {
298
        [&, this](bool vary) NOEXCEPT
×
299
        {
300
            // This is never executed in standard scripts.
301
            value = make_external(chunk::from_bool(vary), tether_);
×
302
        },
303
        [&](int64_t vary) NOEXCEPT
114✔
304
        {
305
            // This is never executed in standard scripts.
306
            value = make_external(chunk::from_integer(vary), tether_);
38✔
307
        },
308
        [&](const chunk_xptr& vary) NOEXCEPT
3,540✔
309
        {
310
            // This is the canonical use case.
311
            value = vary;
3,540✔
312
        }
313
    }, top());
314

315
    return value;
3,578✔
316
}
317

318
// Could use peek_chunk but this overload skips allocation and tethering.
319
template <typename Container>
320
inline size_t stack<Container>::peek_size() const NOEXCEPT
56✔
321
{
322
    using namespace number;
323
    size_t value{};
56✔
324

325
    std::visit(overload
56✔
326
    {
327
        [&](bool vary) NOEXCEPT
×
328
        {
329
            // This is never executed in standard scripts.
330
            value = chunk::from_bool(vary).size();
×
331
        },
332
        [&](int64_t vary) NOEXCEPT
10✔
333
        {
334
            // This is never executed in standard scripts.
335
            value = chunk::from_integer(vary).size();
10✔
336
        },
337
        [&](const chunk_xptr& vary) NOEXCEPT
46✔
338
        {
339
            // This is never executed in standard scripts.
340
            value = vary->size();
46✔
341
        }
342
    }, top());
343

344
    return value;
56✔
345
}
346

347
/// Static variant compare with conversion.
348
/// Integers are unconstrained as these are stack chunk equality comparisons.
349
template <typename Container>
350
inline bool stack<Container>::equal_chunks(const stack_variant& left,
556✔
351
    const stack_variant& right) NOEXCEPT
352
{
353
    enum stack_type { bool_, int64_, pchunk_ };
354
    static_assert(std::variant_size<stack_variant>::value == 3u);
355
    const auto right_type = static_cast<stack_type>(right.index());
556✔
356

357
    using namespace number;
358
    auto same{ true };
556✔
359

360
    // Methods bound at compile time (free).
361
    // One runtime switch on variant index (cheap).
362
    // bool/int conversions are compile-time (free).
363
    // chunk conversions reduce to conventional bitcoin design.
364
    std::visit(overload
556✔
365
    {
366
        // This is never executed in standard scripts.
367
        [&](bool vary) NOEXCEPT
×
368
        {
369
            switch (right_type)
12✔
370
            {
371
                case bool_:
×
372
                    same = std::get<bool>(right) == vary;
×
373
                    break;
×
374
                case int64_:
12✔
375
                    same = std::get<int64_t>(right) == boolean::to_integer(vary);
12✔
376
                    break;
12✔
377
                case pchunk_:
×
378
                    same = *std::get<chunk_xptr>(right) == chunk::from_bool(vary);
×
379
            }
380
        },
381

382
        // This is never executed in standard scripts.
383
        [&](int64_t vary) NOEXCEPT
28✔
384
        {
385
            switch (right_type)
346✔
386
            {
387
                case bool_:
×
388
                    same = boolean::to_integer(std::get<bool>(right)) == vary;
×
389
                    break;
×
390
                case int64_:
318✔
391
                    same = std::get<int64_t>(right) == vary;
318✔
392
                    break;
318✔
393
                case pchunk_:
28✔
394
                    same = *std::get<chunk_xptr>(right) == chunk::from_integer(vary);
56✔
395
            }
396
        },
397

398
        // This is the canonical use case.
399
        [&](chunk_xptr vary) NOEXCEPT
198✔
400
        {
401
            switch (right_type)
198✔
402
            {
403
                case bool_:
404
                    // This is never executed in standard scripts.
405
                    same = chunk::from_bool(std::get<bool>(right)) == *vary;
×
406
                    break;
×
407
                case int64_:
408
                    // This is never executed in standard scripts.
409
                    same = chunk::from_integer(std::get<int64_t>(right)) == *vary;
14✔
410
                    break;
14✔
411
                case pchunk_:
412
                    // This is the canonical use case.
413
                    same = *std::get<chunk_xptr>(right) == *vary;
184✔
414
            }
415
        }
416
    }, left);
417

418
    return same;
556✔
419
}
420

421
} // namespace machine
422
} // namespace system
423
} // namespace libbitcoin
424

425
#endif
426

427
// Tethering Considerations
428
//
429
// Hash results and int/bool->chunks are saved using a shared_ptr vector.
430
// The tether is not garbage-collected (until destruct) as this is a space-
431
// time performance tradeoff. The maximum number of constructable chunks is
432
// bound by the script size limit. A standard in/out script pair tethers
433
// only one chunk, the computed hash.
434
//
435
// The following script operations will ALWAYS tether chunks, as the result
436
// of a computed hash is *pushed* to the weak pointer (xptr) variant stack.
437
//
438
// op_ripemd160         (1)
439
// op_sha1              (1)
440
// op_sha256            (1)
441
// op_hash160           (1)
442
// op_hash256           (1)
443
//
444
// The following script operations will ONLY tether chunks in the case where
445
// the *popped* element was originally bool/int64_t but required as chunk.
446
// This is never the case in standard scripts.
447
//
448
// op_ripemd160         (0..1)
449
// op_sha1              (0..1)
450
// op_sha256            (0..1)
451
// op_hash160           (0..1)
452
// op_hash256           (0..1)
453
// op_size              (0..1)
454
// op_check_sig         (0..2, and m (endorsements) + n (keys))
455
// op_check_sig_verify  (0..2, and m (endorsements) + n (keys))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc