glinebuffer.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
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 unsigned long GNet::LineBuffer::m_limit = G::limits::net_line_limit ; // denial-of-service line-length limit
29 
30 GNet::LineBuffer::LineBuffer( const std::string & eol , bool do_throw ) :
31  m_eol(eol) ,
32  m_eol_length(eol.length()) ,
33  m_p(std::string::npos) ,
34  m_current_valid(false) ,
35  m_throw(do_throw) ,
36  m_locked(false)
37 {
38  G_ASSERT( !eol.empty() ) ;
39 }
40 
41 void GNet::LineBuffer::lock()
42 {
43  m_locked = true ;
44 }
45 
46 void GNet::LineBuffer::unlock( std::string::size_type n )
47 {
48  m_locked = false ;
49  m_current_valid = false ;
50  m_store.erase( 0U , n ) ;
51  if( m_p != std::string::npos )
52  m_p -= n ;
53 }
54 
56 {
57  G_ASSERT( p != NULL ) ;
58  check( n ) ;
59  m_store.append( p , n ) ;
60  fix( n ) ;
61 }
62 
63 void GNet::LineBuffer::add( const std::string & s )
64 {
65  check( s.length() ) ;
66  m_store.append( s ) ;
67  fix( s.length() ) ;
68 }
69 
70 void GNet::LineBuffer::check( std::string::size_type n )
71 {
72  G_ASSERT( !m_locked ) ;
73  if( (m_store.length()+n) > m_limit )
74  {
75  std::string::size_type total = m_store.size() + m_current.size() + n ;
76 
77  m_p = std::string::npos ;
78  m_current.erase() ;
79  m_current_valid = false ;
80  m_store.erase() ;
81 
82  if( m_throw )
83  throw Overflow() ;
84  else
85  G_ERROR( "GNet::LineBuffer::check: line too long: discarding " << total << " bytes" ) ;
86  }
87 }
88 
89 void GNet::LineBuffer::fix( std::string::size_type n )
90 {
91  if( m_p == std::string::npos )
92  {
93  std::string::size_type start = m_store.length() - n ;
94  start = (start+1U) > m_eol_length ? (start+1U-m_eol_length) : 0U ;
95  m_p = m_store.find( m_eol , start ) ;
96  }
97 }
98 
100 {
101  return m_p != std::string::npos ;
102 }
103 
104 const std::string & GNet::LineBuffer::current() const
105 {
106  G_ASSERT( m_p != std::string::npos ) ;
107  G_ASSERT( !m_locked ) ;
108 
109  if( ! m_current_valid )
110  {
111  LineBuffer * this_ = const_cast<LineBuffer*>(this) ;
112  this_->m_current = std::string( m_store , 0U , m_p ) ;
113  this_->m_current_valid = true ;
114  }
115  return m_current ;
116 }
117 
119 {
120  G_ASSERT( m_p != std::string::npos ) ;
121  G_ASSERT( !m_locked ) ;
122 
123  m_current.erase() ;
124  m_current_valid = false ;
125  m_store.erase( 0U , m_p + m_eol_length ) ;
126  m_p = m_store.find( m_eol ) ;
127 }
128 
130 {
131  std::string s( current() ) ;
132  discard() ;
133  return s ;
134 }
135 
136 // ==
137 
139 {
140  return m_b.m_p != std::string::npos ;
141 }
142 
143 const std::string & GNet::LineBufferIterator::line()
144 {
145  // this is optimised code -- the optimisation applies in high-throughput
146  // situations when the network code dumps in more than one line of
147  // text from each network event -- the discard() above is effectively
148  // done once per add() rather than once per line
149  const std::string::size_type n = m_b.m_p - m_n ;
150  m_b.m_current.resize( n ) ;
151  m_b.m_current.replace( 0U , n , m_b.m_store.data() + m_n , n ) ;
152  m_n = m_b.m_p + m_b.m_eol_length ;
153  m_b.m_p = m_n == m_store_length ? std::string::npos : m_b.m_store.find(m_b.m_eol,m_n) ;
154  return m_b.m_current ;
155 }
156 
void add(const std::string &segment)
Adds a data segment.
Definition: glinebuffer.cpp:63
const std::string & current() const
Returns the current line, without extracting it.
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
bool more() const
Returns true if there is a line() to be had.
LineBuffer(const std::string &eol=std::string("\n"), bool do_throw_on_overflow=false)
Constructor.
Definition: glinebuffer.cpp:30
#define G_ASSERT(test)
Definition: gassert.h:30
const std::string & line()
Returns the current line and increments the iterator.
#define G_ERROR(expr)
Definition: glog.h:108
A class which does line buffering.
Definition: glinebuffer.h:52
std::string line()
Extracts a line and returns it as a string.
bool more() const
Returns true if there are complete line(s) to be extracted.
Definition: glinebuffer.cpp:99
void discard()
Discards the current line.