Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

chars_seq.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Fri Apr 22 13:38:08 EDT 2005 by irina.furman@intel.com 
00020 //      modified on Tue Mar 22 14:26:53 EST 2005 by ken@xorian.net         
00021 //      modified on Wed Jul 31 13:28:32 EDT 2002 by kcschalk@shr.intel.com 
00022 //      modified on Tue Feb  8 11:56:21 PST 2000 by mann  
00023 //      modified on Thu Jun 27 18:00:28 PDT 1996 by heydon
00024 //      modified on Thu Jul 20 12:26:06 PDT 1995 by levin
00025 
00026 //  *********************************
00027 //  *  Implementation of chars_seq  *
00028 //  *********************************
00029 
00030 #include "chars_seq.H"
00031 
00032 typedef chars_seq::chars chars;
00033 typedef chars_seq_impl::rep rep;
00034 
00035 
00036 //  **************
00037 //  *  Utilities  *
00038 //  ***************
00039 
00040 
00041 // Return the smallest multiple of 'm' at least as large as 'v'. This is
00042 // equivalent to 'ceiling((float)v/(float)m) * m'.
00043 inline int round_up(int v, int m) { return (((v + m - 1) / m) * m); }
00044 
00045 rep *chars_seq_impl::allocate_buffer(int len_hint, int bytes)
00046 {
00047   // Allocate a buffer with room for "len_hint" pointers and "bytes"
00048   // characters of string bodies (excluding terminators), ensuring that
00049   // the buffer is aligned for pointer storage.
00050   int size = chars_seq::min_size(len_hint) + round_up(bytes, sizeof(chars));
00051   char* buffer = (char *) NEW_PTRFREE_ARRAY(chars, size/sizeof(chars));
00052   rep *p = (rep *) buffer;
00053   p->h.storage = grow;
00054   p->h.len = 0;
00055   p->h.bodies = p->h.limit = &buffer[size];
00056   return p;
00057 }
00058 
00059 void chars_seq_impl::expand(rep *&p, int len_hint, int min_body_add)
00060 {
00061   if (p->h.storage == manual)
00062     throw(chars_seq::failure("Buffer has insufficient space."));
00063   // Expand storage.  If len_hint is non-negative, it specifies a new length
00064   // hint.  If min_body_add is non-negative, the string space either will be
00065   // doubled (compared to the presently used amount), or increased in size
00066   // by min_body_add, whichever produces a larger resulting buffer.
00067   int new_len = len_hint < 0 ? p->h.len : len_hint;
00068   int old_body_sz = p->h.limit - p->h.bodies;
00069   int old_char_cnt = old_body_sz - p->h.len; // subtract off NULL terminators
00070   int new_char_cnt = old_char_cnt +
00071     ((min_body_add >= 0) ? max(old_char_cnt, min_body_add) : 0);
00072 
00073   // allocate a new buffer, subtracting out terminators of existing strings
00074   rep *new_p = allocate_buffer(new_len, new_char_cnt);
00075 
00076   new_p->h.storage = p->h.storage;
00077   new_p->h.len = p->h.len;
00078   new_p->h.bodies -= old_body_sz;
00079 
00080   // Copy string bodies (if any) as a unit
00081   memcpy(new_p->h.bodies, p->h.bodies, old_body_sz);
00082 
00083   // Copy pointers one at a time, relocating if necessary.
00084   chars *seq = &p->base;
00085   chars *new_seq = &new_p->base;
00086   for (int i = 0; i < p->h.len; i++) {
00087     assert(seq[i] >= p->h.bodies && seq[i] < p->h.limit);
00088 
00089     // Compute its offset from the end of the original block.
00090     int offset = p->h.limit - seq[i];
00091     // Compute the new pointer based on the offset and the end of
00092     // the new block.
00093     new_seq[i] = new_p->h.limit - offset;
00094 
00095     // Additional paranoia, probably warranted given the pointer
00096     // math.
00097     assert((new_seq[i] >= new_p->h.bodies) &&
00098            (new_seq[i] < new_p->h.limit));
00099 
00100     seq[i] = (char *)NULL; // help garbage collector
00101   }
00102   delete[] (chars *)p;
00103   p = new_p;
00104 }
00105 
00106 
00107 //  **********************************
00108 //  *  Constructors and Destructors  *
00109 //  **********************************
00110 
00111 chars_seq::chars_seq(int len_hint, int bytes)
00112 {
00113   if (len_hint == 0 && bytes == 0)
00114     // Defer allocation until needed.  The first allocation might
00115     // be via SRPC::recv_chars_seq, which can allocate precisely the
00116     // right amount.
00117     p = NULL;
00118   else p = chars_seq_impl::allocate_buffer(len_hint, bytes);
00119 }
00120 
00121 chars_seq::chars_seq(void **buffer, int bytes) throw (chars_seq::failure)
00122 {
00123   p = (rep *) buffer;
00124   p->h.storage = chars_seq_impl::manual;
00125   p->h.len = 0;
00126   p->h.bodies = p->h.limit = (char *)buffer + bytes;
00127 }
00128 
00129 chars_seq::chars_seq(const char **seq, int len) throw(chars_seq::failure)
00130 {
00131   int total_len = 0;
00132   for (int i = 0; i < len; i++) total_len += strlen(seq[i]);
00133   p = chars_seq_impl::allocate_buffer(len, total_len);
00134   for (int i = 0; i < len; i++) append(seq[i]);
00135 }
00136 
00137 chars_seq::~chars_seq()
00138 {
00139   if (p == NULL || p->h.storage == chars_seq_impl::manual) return;
00140   delete[] (chars *)p;
00141 }
00142 
00143 /* static */ int chars_seq::min_size(int len)
00144 {
00145   // This calculation allows for the possibility that padding will be
00146   // inserted between 'h' and 'base'.  It does this by computing the
00147   // size of 'rep', which includes (in effect) a single-element sequence
00148   // (i.e., base), then adding additional space for len-1 additional pointers
00149   // as well as space for a null terminator on each of the 'len' strings.
00150   return
00151     round_up(sizeof(chars_seq_impl::rep) + (len-1)*sizeof(chars) + len*1,
00152             sizeof(chars));
00153 }
00154 
00155 //  ***************************
00156 //  *  Sequence modification  *
00157 //  ***************************
00158 
00159 void chars_seq::append(const chars s) throw(chars_seq::failure)
00160 {
00161   if (p == NULL) p = chars_seq_impl::allocate_buffer();
00162   int s_len = strlen(s);
00163   int bytes_for_body = s_len + 1; // include NULL terminator
00164   chars *seq = &p->base;    // base of pointer vector
00165   char *s_copy = p->h.bodies - bytes_for_body;
00166 
00167   // The following pointer comparison is portable, per ARM 5.9, since
00168   // both point into the same array (or one beyond its end).
00169   if (&seq[p->h.len+1] > (chars *)s_copy) {
00170     // No space in buffer for string body and pointer.  Expand first.
00171     chars_seq_impl::expand(p, 1+2*p->h.len, s_len);
00172     // Recompute pointers affected by 'expand'
00173     seq = &p->base;
00174     s_copy = p->h.bodies - bytes_for_body;
00175   }
00176   // Now there must be room, regardless of whether we expanded or not.
00177   assert(&seq[p->h.len+1] <=  (chars *)s_copy);
00178 
00179   memcpy(s_copy, s, bytes_for_body);
00180   seq[p->h.len] = p->h.bodies = s_copy;
00181   p->h.len++;
00182 }
00183 
00184 void chars_seq::append(const Text &t) throw(chars_seq::failure)
00185 {
00186   append(t.chars());
00187 }

Generated on Mon May 8 00:48:56 2006 for Vesta by  doxygen 1.4.2