1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/tools/flip_server/ring_buffer.h"
6#include "base/logging.h"
7
8namespace net {
9
10RingBuffer::RingBuffer(int buffer_size)
11    : buffer_(new char[buffer_size]),
12      buffer_size_(buffer_size),
13      bytes_used_(0),
14      read_idx_(0),
15      write_idx_(0) {
16}
17
18RingBuffer::~RingBuffer() {}
19
20////////////////////////////////////////////////////////////////////////////////
21
22int RingBuffer::ReadableBytes() const {
23  return bytes_used_;
24}
25
26////////////////////////////////////////////////////////////////////////////////
27
28int RingBuffer::BufferSize() const {
29  return buffer_size_;
30}
31
32////////////////////////////////////////////////////////////////////////////////
33
34int RingBuffer::BytesFree() const {
35  return BufferSize() - ReadableBytes();
36}
37
38////////////////////////////////////////////////////////////////////////////////
39
40bool RingBuffer::Empty() const {
41  return ReadableBytes() == 0;
42}
43
44////////////////////////////////////////////////////////////////////////////////
45
46bool RingBuffer::Full() const {
47  return ReadableBytes() == BufferSize();
48}
49
50////////////////////////////////////////////////////////////////////////////////
51
52// Returns the number of characters written.
53// Appends up-to-'size' bytes to the ringbuffer.
54int RingBuffer::Write(const char* bytes, int size) {
55  CHECK_GE(size, 0);
56#if 1
57  char* wptr;
58  int wsize;
59  GetWritablePtr(&wptr, &wsize);
60  int bytes_remaining = size;
61  int bytes_written = 0;
62
63  while (wsize && bytes_remaining) {
64    if (wsize > bytes_remaining) {
65      wsize = bytes_remaining;
66    }
67    memcpy(wptr, bytes + bytes_written, wsize);
68    bytes_written += wsize;
69    bytes_remaining -= wsize;
70    AdvanceWritablePtr(wsize);
71    GetWritablePtr(&wptr, &wsize);
72  }
73  return bytes_written;
74#else
75  const char* p = bytes;
76
77  int bytes_to_write = size;
78  int bytes_available = BytesFree();
79  if (bytes_available < bytes_to_write) {
80    bytes_to_write = bytes_available;
81  }
82  const char* end = bytes + bytes_to_write;
83
84  while (p != end) {
85    this->buffer_[this->write_idx_] = *p;
86    ++p;
87    ++this->write_idx_;
88    if (this->write_idx_ >= this->buffer_size_) {
89      this->write_idx_ = 0;
90    }
91  }
92  bytes_used_ += bytes_to_write;
93  return bytes_to_write;
94#endif
95}
96
97////////////////////////////////////////////////////////////////////////////////
98
99// Sets *ptr to the beginning of writable memory, and sets *size to the size
100// available for writing using this pointer.
101void RingBuffer::GetWritablePtr(char** ptr, int* size) const {
102  *ptr = buffer_.get() + write_idx_;
103
104  if (bytes_used_ == buffer_size_) {
105    *size = 0;
106  } else if (read_idx_ > write_idx_) {
107    *size = read_idx_ - write_idx_;
108  } else {
109    *size = buffer_size_ - write_idx_;
110  }
111}
112
113////////////////////////////////////////////////////////////////////////////////
114
115// Sets *ptr to the beginning of readable memory, and sets *size to the size
116// available for reading using this pointer.
117void RingBuffer::GetReadablePtr(char** ptr, int* size) const {
118  *ptr = buffer_.get() + read_idx_;
119
120  if (bytes_used_ == 0) {
121    *size = 0;
122  } else if (write_idx_ > read_idx_) {
123    *size = write_idx_ - read_idx_;
124  } else {
125    *size = buffer_size_ - read_idx_;
126  }
127}
128
129////////////////////////////////////////////////////////////////////////////////
130
131// returns the number of bytes read into
132int RingBuffer::Read(char* bytes, int size) {
133  CHECK_GE(size, 0);
134#if 1
135  char* rptr;
136  int rsize;
137  GetReadablePtr(&rptr, &rsize);
138  int bytes_remaining = size;
139  int bytes_read = 0;
140
141  while (rsize && bytes_remaining) {
142    if (rsize > bytes_remaining) {
143      rsize = bytes_remaining;
144    }
145    memcpy(bytes + bytes_read, rptr, rsize);
146    bytes_read += rsize;
147    bytes_remaining -= rsize;
148    AdvanceReadablePtr(rsize);
149    GetReadablePtr(&rptr, &rsize);
150  }
151  return bytes_read;
152#else
153  char* p = bytes;
154  int bytes_to_read = size;
155  int bytes_used = ReadableBytes();
156  if (bytes_used < bytes_to_read) {
157    bytes_to_read = bytes_used;
158  }
159  char* end = bytes + bytes_to_read;
160
161  while (p != end) {
162    *p = this->buffer_[this->read_idx_];
163    ++p;
164    ++this->read_idx_;
165    if (this->read_idx_ >= this->buffer_size_) {
166      this->read_idx_ = 0;
167    }
168  }
169  this->bytes_used_ -= bytes_to_read;
170  return bytes_to_read;
171#endif
172}
173
174////////////////////////////////////////////////////////////////////////////////
175
176void RingBuffer::Clear() {
177  bytes_used_ = 0;
178  write_idx_ = 0;
179  read_idx_ = 0;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183
184bool RingBuffer::Reserve(int size) {
185  DCHECK(size > 0);
186  char* write_ptr = NULL;
187  int write_size = 0;
188  GetWritablePtr(&write_ptr, &write_size);
189
190  if (write_size < size) {
191    char* read_ptr = NULL;
192    int read_size = 0;
193    GetReadablePtr(&read_ptr, &read_size);
194    if (size <= BytesFree()) {
195      // The fact that the total Free size is big enough but writable size is
196      // not means that the writeable region is broken into two pieces: only
197      // possible if the read_idx < write_idx. If write_idx < read_idx, then
198      // the writeable region must be contiguous: [write_idx, read_idx). There
199      // is no work to be done for the latter.
200      DCHECK(read_idx_ <= write_idx_);
201      DCHECK(read_size == ReadableBytes());
202      if (read_idx_ < write_idx_) {
203        // Writeable area fragmented, consolidate it.
204        memmove(buffer_.get(), read_ptr, read_size);
205        read_idx_ = 0;
206        write_idx_ = read_size;
207      } else if (read_idx_ == write_idx_) {
208        // No unconsumed data in the buffer, simply reset the indexes.
209        DCHECK(ReadableBytes() == 0);
210        read_idx_ = 0;
211        write_idx_ = 0;
212      }
213    } else {
214      Resize(ReadableBytes() + size);
215    }
216  }
217  DCHECK_LE(size, buffer_size_ - write_idx_);
218  return true;
219}
220
221////////////////////////////////////////////////////////////////////////////////
222
223void RingBuffer::AdvanceReadablePtr(int amount_to_consume) {
224  CHECK_GE(amount_to_consume, 0);
225  if (amount_to_consume >= bytes_used_) {
226    Clear();
227    return;
228  }
229  read_idx_ += amount_to_consume;
230  read_idx_ %= buffer_size_;
231  bytes_used_ -= amount_to_consume;
232}
233
234////////////////////////////////////////////////////////////////////////////////
235
236void RingBuffer::AdvanceWritablePtr(int amount_to_produce) {
237  CHECK_GE(amount_to_produce, 0);
238  CHECK_LE(amount_to_produce, BytesFree());
239  write_idx_ += amount_to_produce;
240  write_idx_ %= buffer_size_;
241  bytes_used_ += amount_to_produce;
242}
243
244////////////////////////////////////////////////////////////////////////////////
245
246void RingBuffer::Resize(int buffer_size) {
247  CHECK_GE(buffer_size, 0);
248  if (buffer_size == buffer_size_) return;
249
250  char* new_buffer = new char[buffer_size];
251  if (buffer_size < bytes_used_) {
252    // consume the oldest data.
253    AdvanceReadablePtr(bytes_used_ - buffer_size);
254  }
255
256  int bytes_written = 0;
257  int bytes_used = bytes_used_;
258  while (true) {
259    int size;
260    char* ptr;
261    GetReadablePtr(&ptr, &size);
262    if (size == 0) break;
263    if (size > buffer_size) {
264      size = buffer_size;
265    }
266    memcpy(new_buffer + bytes_written, ptr, size);
267    bytes_written += size;
268    AdvanceReadablePtr(size);
269  }
270  buffer_.reset(new_buffer);
271
272  buffer_size_ = buffer_size;
273  bytes_used_ = bytes_used;
274  read_idx_ = 0;
275  write_idx_ = bytes_used_ % buffer_size_;
276}
277
278}  // namespace net
279
280