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