/src/serenity/AK/CircularBuffer.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2022, Lucas Chollet <lucas.chollet@free.fr> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/ByteBuffer.h> |
10 | | #include <AK/Error.h> |
11 | | #include <AK/HashMap.h> |
12 | | #include <AK/Noncopyable.h> |
13 | | #include <AK/Vector.h> |
14 | | |
15 | | namespace AK { |
16 | | |
17 | | class CircularBuffer { |
18 | | AK_MAKE_NONCOPYABLE(CircularBuffer); |
19 | | AK_MAKE_DEFAULT_MOVABLE(CircularBuffer); |
20 | | |
21 | | public: |
22 | | static ErrorOr<CircularBuffer> create_empty(size_t size); |
23 | | static ErrorOr<CircularBuffer> create_initialized(ByteBuffer); |
24 | | |
25 | 317k | ~CircularBuffer() = default; |
26 | | |
27 | | size_t write(ReadonlyBytes bytes); |
28 | | Bytes read(Bytes bytes); |
29 | | ErrorOr<void> discard(size_t discarded_bytes); |
30 | | ErrorOr<size_t> fill_from_stream(Stream&); |
31 | | ErrorOr<size_t> flush_to_stream(Stream&); |
32 | | |
33 | | /// Compared to `read()`, this starts reading from an offset that is `distance` bytes |
34 | | /// before the current write pointer and allows for reading already-read data. |
35 | | ErrorOr<Bytes> read_with_seekback(Bytes bytes, size_t distance) const; |
36 | | |
37 | | ErrorOr<size_t> copy_from_seekback(size_t distance, size_t length); |
38 | | |
39 | | [[nodiscard]] size_t empty_space() const; |
40 | | [[nodiscard]] size_t used_space() const; |
41 | | [[nodiscard]] size_t capacity() const; |
42 | | [[nodiscard]] size_t seekback_limit() const; |
43 | | |
44 | | Optional<size_t> offset_of(StringView needle, Optional<size_t> from = {}, Optional<size_t> until = {}) const; |
45 | | |
46 | | void clear(); |
47 | | |
48 | | protected: |
49 | | CircularBuffer(ByteBuffer); |
50 | | |
51 | | [[nodiscard]] bool is_wrapping_around() const; |
52 | | |
53 | | [[nodiscard]] Bytes next_write_span(); |
54 | | [[nodiscard]] ReadonlyBytes next_read_span(size_t offset = 0) const; |
55 | | [[nodiscard]] ReadonlyBytes next_seekback_span(size_t distance) const; |
56 | | |
57 | | ByteBuffer m_buffer {}; |
58 | | |
59 | | size_t m_reading_head {}; |
60 | | size_t m_used_space {}; |
61 | | size_t m_seekback_limit {}; |
62 | | }; |
63 | | |
64 | | class SearchableCircularBuffer : public CircularBuffer { |
65 | | public: |
66 | | static ErrorOr<SearchableCircularBuffer> create_empty(size_t size); |
67 | | static ErrorOr<SearchableCircularBuffer> create_initialized(ByteBuffer); |
68 | | |
69 | | [[nodiscard]] size_t search_limit() const; |
70 | | |
71 | | // These functions update the read pointer, so we need to hash any data that we have processed. |
72 | | ErrorOr<Bytes> read(Bytes bytes); |
73 | | ErrorOr<void> discard(size_t discarded_bytes); |
74 | | ErrorOr<size_t> flush_to_stream(Stream& stream); |
75 | | |
76 | | struct Match { |
77 | | size_t distance; |
78 | | size_t length; |
79 | | }; |
80 | | /// This searches the seekback buffer (between read head and limit) for occurrences where it matches the next `length` bytes from the read buffer. |
81 | | /// Supplying any hints will only consider those distances, in case existing offsets need to be validated. |
82 | | /// Note that, since we only start searching at the read head, the length between read head and write head is excluded from the distance. |
83 | | Optional<Match> find_copy_in_seekback(size_t maximum_length, size_t minimum_length = 2); |
84 | | Optional<Match> find_copy_in_seekback(ReadonlySpan<size_t> distances, size_t maximum_length, size_t minimum_length = 2) const; |
85 | | |
86 | | // The chunk size for which the hash table holds hashes. |
87 | | // This is nice for users to know, as picking a minimum match length that is |
88 | | // equal or greater than this allows us to completely skip a slow memory search. |
89 | | static constexpr size_t HASH_CHUNK_SIZE = 3; |
90 | | |
91 | | private: |
92 | | // Note: This function has a similar purpose as next_seekback_span, but they differ in their reference point. |
93 | | // Seekback operations start counting their distance at the write head, while search operations start counting their distance at the read head. |
94 | | [[nodiscard]] ReadonlyBytes next_search_span(size_t distance) const; |
95 | | |
96 | | SearchableCircularBuffer(ByteBuffer); |
97 | | |
98 | | size_t m_hash_head {}; |
99 | | HashMap<unsigned, size_t> m_hash_location_map; |
100 | | HashMap<size_t, size_t> m_location_chain_map; |
101 | | |
102 | | ErrorOr<void> insert_location_hash(ReadonlyBytes value, size_t raw_offset); |
103 | | ErrorOr<void> hash_new_bytes(); |
104 | | }; |
105 | | |
106 | | } |