VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
glinebuffer.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // glinebuffer.cpp
19 //
20 
21 #include "gdef.h"
22 #include "glimits.h"
23 #include "gnet.h"
24 #include "glinebuffer.h"
25 #include "gdebug.h"
26 #include "gassert.h"
27 
28 static const unsigned long line_limit = G::limits::net_line_limit ;
29 
31  m_iterator(nullptr) ,
32  m_auto(true) ,
33  m_eol("\n") ,
34  m_throw_on_overflow(false) ,
35  m_expect(0U)
36 {
37 }
38 
39 GNet::LineBuffer::LineBuffer( const std::string & eol , bool do_throw ) :
40  m_iterator(nullptr) ,
41  m_auto(false) ,
42  m_eol(eol) ,
43  m_throw_on_overflow(do_throw) ,
44  m_expect(0U)
45 {
46  G_ASSERT( !eol.empty() ) ;
47 }
48 
49 size_t GNet::LineBuffer::lock( LineBufferIterator * iterator )
50 {
51  if( m_iterator != nullptr ) throw Error("double lock") ;
52  m_iterator = iterator ;
53 
54  detect() ;
55  size_t expect = m_expect ;
56  m_expect = 0U ;
57  return expect ;
58 }
59 
60 void GNet::LineBuffer::unlock( LineBufferIterator * iterator , size_t discard , size_t expect )
61 {
62  if( m_iterator == nullptr || iterator != m_iterator ) throw Error("double unlock") ;
63  m_iterator = nullptr ;
64  m_expect = expect ;
65 
66  G_ASSERT( discard <= m_store.size() ) ;
67  if( discard == m_store.size() )
68  m_store.clear() ;
69  else if( discard != 0U )
70  m_store.erase( 0U , discard ) ;
71 }
72 
73 void GNet::LineBuffer::add( const char * p , std::string::size_type n )
74 {
75  if( check( n ) )
76  m_store.append( p , n ) ;
77 }
78 
79 void GNet::LineBuffer::add( const std::string & s )
80 {
81  add( s.data() , s.size() ) ;
82 }
83 
84 void GNet::LineBuffer::expect( size_t n )
85 {
86  // pass it on, either now or at lock() time
87  if( m_iterator == nullptr )
88  {
89  m_expect = n ;
90  }
91  else
92  {
93  m_expect = 0U ;
94  m_iterator->expect( n ) ;
95  }
96 }
97 
98 void GNet::LineBuffer::detect()
99 {
100  std::string::size_type pos = m_auto ? m_store.find('\n') : std::string::npos ;
101  if( m_auto && pos != std::string::npos )
102  {
103  if( pos > 0U && m_store.at(pos-1U) == '\r' )
104  m_eol = std::string( "\r\n" ) ;
105  m_auto = false ;
106  }
107 }
108 
109 const std::string & GNet::LineBuffer::eol() const
110 {
111  return m_eol ;
112 }
113 
114 bool GNet::LineBuffer::check( size_t n ) const
115 {
116  if( n == 0U ) return false ;
117  if( m_iterator != nullptr ) throw Error( "locked" ) ;
118  bool ok = (m_store.size()+n) <= line_limit ;
119  if( !ok )
120  {
121  if( m_throw_on_overflow )
122  throw Error( "overflow" ) ;
123  else
124  G_ERROR( "GNet::LineBuffer::check: line too long: end-of-line expected: "
125  "have " << m_store.size() << " characters, and adding " << n << " more" ) ;
126  }
127  return ok ;
128 }
129 
130 // ==
131 
133  m_buffer(buffer) ,
134  m_expect(0U) ,
135  m_pos(0U) ,
136  m_eol_size(buffer.m_eol.size()) ,
137  m_line_begin(buffer.m_store.begin()) ,
138  m_line_end(m_line_begin) ,
139  m_line_valid(false)
140 {
141  m_expect = m_buffer.lock( this ) ;
142 }
143 
145 {
146  m_buffer.unlock( this , m_pos , m_expect ) ;
147 }
148 
149 void GNet::LineBufferIterator::expect( size_t n )
150 {
151  m_expect = n ;
152 }
153 
155 {
156  m_line_valid = false ;
157 
158  const size_t npos = std::string::npos ;
159  std::string & store = m_buffer.m_store ;
160  const std::string & eol = m_buffer.m_eol ;
161  size_t eol_size = eol.size() ;
162 
163  G_ASSERT( m_pos <= store.size() ) ;
164  if( m_pos == store.size() )
165  {
166  m_line.clear() ;
167  return false ;
168  }
169 
170  size_t pos = npos ;
171  if( m_expect != 0U )
172  {
173  if( (m_pos+m_expect) <= store.size() )
174  {
175  pos = m_pos + m_expect ;
176  m_expect = 0U ;
177  eol_size = 0U ;
178  }
179  }
180  else
181  {
182  pos = store.find( eol , m_pos ) ;
183  }
184 
185  if( pos == std::string::npos )
186  {
187  m_line.clear() ;
188  return false ;
189  }
190  else
191  {
192  G_ASSERT( pos >= m_pos ) ;
193  const std::string & store = m_buffer.m_store ;
194  m_line_begin = store.begin() + m_pos ;
195  m_line_end = store.begin() + pos ;
196  m_pos = pos + eol_size ;
197  return true ;
198  }
199 }
200 
201 const std::string & GNet::LineBufferIterator::line() const
202 {
203  if( !m_line_valid )
204  {
205  m_line.assign( m_line_begin , m_line_end ) ;
206  m_line_valid = true ;
207  }
208  return m_line ;
209 }
210 
211 std::string::const_iterator GNet::LineBufferIterator::begin() const
212 {
213  return m_line_begin ;
214 }
215 
216 std::string::const_iterator GNet::LineBufferIterator::end() const
217 {
218  return m_line_end ;
219 }
220 
221 /// \file glinebuffer.cpp
const std::string & eol() const
Returns the line-ending.
void add(const std::string &segment)
Adds a data segment.
Definition: glinebuffer.cpp:79
void expect(size_t n)
The next 'n' bytes added and/or extracted are treated as a complete line.
Definition: glinebuffer.cpp:84
std::string::const_iterator end() const
Returns an end iterator for the current line.
An iterator class for GNet::LineBuffer that extracts complete lines.
Definition: glinebuffer.h:102
LineBuffer()
Default constructor for a line buffer that auto-detects either CR or CR-LF line endings based on the ...
Definition: glinebuffer.cpp:30
LineBufferIterator(LineBuffer &)
Constructor.
~LineBufferIterator()
Destructor.
std::string::const_iterator begin() const
Returns a begin iterator for the current line.
const std::string & line() const
Returns the current line.
A class which does line buffering.
Definition: glinebuffer.h:52
bool more()
Returns true if there is a line() to be had.