aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/rust-buffered-queue.h
blob: f56aaff8cc5d362c6ea0ebf4091398612ab6004d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#ifndef RUST_BUFFERED_QUEUE_H
#define RUST_BUFFERED_QUEUE_H

#include <vector>

#include "config.h"
#include "system.h"
// order: config, system

namespace Rust {
// Buffered queue implementation. Items are of type T, queue source is of type
// Source.
template <typename T, typename Source> class buffered_queue
{
public:
  // Construct empty queue from Source& src.
  buffered_queue (Source &src) : source (src), start (0), end (0), buffer () {}

  // Returns token at position start + n (i.e. n tokens ahead).
  T peek (int n)
  {
    // n should not be behind
    gcc_assert (n >= 0);

    int num_queued_items = end - start;
    int num_items_required = n + 1;

    // if required items go past end of queue, add them to queue
    if (num_items_required > num_queued_items)
      {
	int num_items_to_read = num_items_required - num_queued_items;

	// if queue length + extra items is larger than buffer size, expand
	// buffer
	if (end + num_items_to_read > (int) buffer.size ())
	  {
	    // Resize the buffer by 1.5x
	    int new_size = (buffer.size () + num_items_to_read);
	    new_size += (new_size >> 1);

	    // create new queue buffer with new size
	    std::vector<T> new_queue (new_size);
	    std::copy (buffer.begin () + start, buffer.begin () + end,
		       new_queue.begin ());
	    start = 0;
	    end = num_queued_items;

	    // swap member buffer and new queue buffer
	    std::swap (buffer, new_queue);

	    // validate that buffer is large enough now
	    gcc_assert (end + num_queued_items < (int) buffer.size ());
	  }

	// iterate through buffer and invoke operator () on source on values
	// past original end
	for (int i = 0; i < num_items_to_read; i++)
	  {
	    buffer[end + i] = source ();
	  }

	// move end based on additional items added
	end += num_items_to_read;
      }

    gcc_assert (0 <= start);
    gcc_assert (start <= end);
    gcc_assert (end <= (int) buffer.size ());

    gcc_assert (start + n < end);

    // return value at start + n in buffer
    return buffer[start + n];
  }

  // TODO: add faster peek current token to remove overhead of conditional
  // branches?

  // Advances start by n + 1.
  void skip (int n)
  {
    // Call peek to ensure requested n is actually in queue.
    peek (n);

    // Clear values from start to n (inclusive).
    for (int i = 0; i < (n + 1); i++)
      {
	// Clear value at index
	buffer[start + i] = T ();
      }

    // Move start forward by n + 1.
    start += (n + 1);

    // Ensure start is not impossible somehow
    gcc_assert (0 <= start);
    gcc_assert (start <= end);

    // Compact buffer if empty
    if (start == end)
      {
	start = end = 0;
      }
  }

  /* Inserts element at front of vector. Really dirty hack with terrible
   * performance, only use when really needed. */
  void insert_at_front (T elem_to_insert)
  {
    // TODO: test as this may not work properly

    // Insert actual element in buffer at start.
    buffer.insert (buffer.begin (), 1, elem_to_insert);

    // Increase the end number since added element means all others have shifted
    // one along
    end++;
  }

  // Replaces the current value in the buffer. Total HACK.
  void replace_current_value (T replacement)
  {
    // call peek to ensure value exists
    peek (0);

    buffer[start] = replacement;

    // don't move start or end
  }

private:
  // Source of tokens for queue.
  Source &source;

  // Begin of range in buffer, inclusive.
  int start;
  // End of range in buffer, exclusive.
  int end;

  // Queue buffer.
  std::vector<T> buffer;
};
} // namespace Rust

#endif