MutableBufferClass.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Kernel */
40 /* */
41 /* Primitive MutableBuffer Class */
42 /* */
43 /******************************************************************************/
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "RexxCore.h"
49 #include "StringClass.hpp"
50 #include "MutableBufferClass.hpp"
51 #include "ProtectedObject.hpp"
52 #include "StringUtil.hpp"
53 
54 
55 // singleton class instance
57 
58 
59 
60 /**
61  * Create initial class object at bootstrap time.
62  */
64 {
65  CLASS_CREATE(MutableBuffer, "MutableBuffer", RexxClass);
66 }
67 
68 
69 #define DEFAULT_BUFFER_LENGTH 256
70 
71 // in behaviour
72 RexxMutableBuffer *RexxMutableBufferClass::newRexx(RexxObject **args, size_t argc, size_t named_argc)
73 /******************************************************************************/
74 /* Function: Allocate (and initialize) a string object */
75 /******************************************************************************/
76 {
77  RexxString *string;
78  RexxMutableBuffer *newBuffer; /* new mutable buffer object */
79  sizeB_t bufferLength = DEFAULT_BUFFER_LENGTH;
80  sizeB_t defaultSize;
81  if (argc >= 1)
82  {
83  if (args[0] != NULL)
84  {
85  /* force argument to string value */
86  string = stringArgument(args[0], OREF_positional, ARG_ONE);
87  }
88  else
89  {
90  string = OREF_NULLSTRING; /* default to empty content */
91  }
92  }
93  else /* minimum buffer size given? */
94  {
95  string = OREF_NULLSTRING;
96  }
97  ProtectedObject p_string(string);
98 
99  if (argc >= 2)
100  {
101  bufferLength = optionalLengthArgument(args[1], DEFAULT_BUFFER_LENGTH, ARG_TWO);
102  }
103 
104  defaultSize = bufferLength; /* remember initial default size */
105 
106  /* input string longer than demanded */
107  /* minimum size? expand accordingly */
108  if (string->getBLength() > bufferLength)
109  {
110  bufferLength = string->getBLength();
111  }
112  /* allocate the new object */
113  newBuffer = new ((RexxClass *)this) RexxMutableBuffer(bufferLength, defaultSize);
114  newBuffer->setBLength(string->getBLength());
115  /* copy the content */
116  newBuffer->copyData(0, string->getStringData(), string->getBLength());
117 
118  ProtectedObject p_newBuffer(newBuffer);
119  newBuffer->sendMessage(OREF_INIT, args, argc > 2 ? argc - 2 : 0, named_argc);
120  return newBuffer;
121 }
122 
123 
124 /**
125  * Default constructor.
126  */
128 {
129  bufferLength = DEFAULT_BUFFER_LENGTH; /* save the length of the buffer */
130  defaultSize = bufferLength; /* store the default buffer size */
131  // NB: we clear this before we allocate the new buffer because allocating the
132  // new buffer might trigger a garbage collection, causing us to mark bogus
133  // reference.
134  data = OREF_NULL;
136  data->setDataLength(0); // strange to have dataLength equal to bufferSize by default... I assign 0 instead.
137  dataBLength = 0;
138 }
139 
140 
141 /**
142  * Constructor with explicitly set size and default.
143  *
144  * @param l Initial length.
145  * @param d The explicit default size.
146  */
148 {
149  bufferLength = l; /* save the length of the buffer */
150  defaultSize = d; /* store the default buffer size */
151  // NB: As in the default constructor, we clear this before we allocate the
152  // new buffer in case garbage collection is triggered.
153  data = OREF_NULL;
155  this->setBLength(0);
156 }
157 
158 
159 /**
160  * Create a new mutable buffer object from a potential subclass.
161  *
162  * @param size The size of the buffer object.
163  *
164  * @return A new instance of a mutable buffer, with the default class
165  * behaviour.
166  */
167 void *RexxMutableBuffer::operator new(size_t size)
168 {
169  return new_object(size, T_MutableBuffer);
170 }
171 
172 /**
173  * Create a new mutable buffer object from a potential subclass.
174  *
175  * @param size The size of the buffer object.
176  * @param bufferClass
177  * The class of the buffer object.
178  *
179  * @return A new instance of a mutable buffer, with the target class
180  * behaviour.
181  */
182 void *RexxMutableBuffer::operator new(size_t size, RexxClass *bufferClass)
183 {
184  RexxObject * newObj = new_object(size, T_MutableBuffer);
185  newObj->setBehaviour(bufferClass->getInstanceBehaviour());
186  return newObj;
187 }
188 
189 
190 void RexxMutableBuffer::live(size_t liveMark)
191 /******************************************************************************/
192 /* Function: Normal garbage collection live marking */
193 /******************************************************************************/
194 {
195  memory_mark(this->objectVariables);
196  memory_mark(this->data);
197 }
198 
200 /******************************************************************************/
201 /* Function: Generalized object marking */
202 /******************************************************************************/
203 {
204  memory_mark_general(this->objectVariables);
205  memory_mark_general(this->data);
206 }
207 
208 
210 /******************************************************************************/
211 /* Function: Flatten a mutable buffer */
212 /******************************************************************************/
213 {
215 
216  flatten_reference(newThis->data, envelope);
217  flatten_reference(newThis->objectVariables, envelope);
218 
220 }
221 
223 /******************************************************************************/
224 /* Function: copy an object */
225 /******************************************************************************/
226 {
227 
228  RexxMutableBuffer *newObj = (RexxMutableBuffer *)this->clone();
229 
230  /* see the comments in ::newRexx()!! */
231  newObj->data = new_buffer(bufferLength);
232  newObj->setBLength(this->dataBLength);
233  newObj->copyData(0, data->getData(), bufferLength);
234 
235  newObj->defaultSize = this->defaultSize;
236  newObj->bufferLength = this->bufferLength;
237 
238  return newObj;
239 }
240 
242 /******************************************************************************/
243 /* Function: append to the mutable buffer */
244 /******************************************************************************/
245 {
246  sizeB_t resultLength = this->dataBLength + addedLength;
247 
248  if (resultLength > bufferLength)
249  { /* need to enlarge? */
250  bufferLength *= 2; /* double the buffer */
251  if (bufferLength < resultLength)
252  { /* still too small? use new length */
253  bufferLength = resultLength;
254  }
255 
256  RexxBuffer *newBuffer = new_buffer(bufferLength);
257  // copy the data into the new buffer
258  newBuffer->copyData(0, data->getData(), dataBLength);
259  newBuffer->setDataLength(data->getDataLength());
260  // replace the old data buffer
261  OrefSet(this, this->data, newBuffer);
262  }
263 }
264 
265 
266 /**
267  * Set the length of the data in the buffer. The limit is
268  * the current capacity of the buffer. If the length is
269  * extended beyond the current length, the extra characters
270  * of the buffer will be filled with nulls.
271  *
272  * @param newLength The new datalength. This is capped to the capacity of
273  * the buffer.
274  *
275  * @return The actual length the data has been set to. If the
276  * target length is greater than the capacity, the capacity
277  * value is returned.
278  */
280 {
281  // cap the data length at the capacity
282  sizeB_t capacity = this->getCapacity();
283  if (newLength > capacity)
284  {
285  newLength = capacity;
286  }
287 
288  sizeB_t oldLength = this->getBLength();
289  // set the new buffer length
290  dataBLength = newLength;
291  // do we need to pad?
292  if (newLength > oldLength)
293  {
294  this->setData(oldLength, '\0', newLength - oldLength);
295  }
296 
297  return newLength;
298 }
299 
300 /**
301  * Set the capacity of the buffer.
302  *
303  * @param newLength The new buffer length
304  *
305  * @return The pointer to the data area in the buffer.
306  */
308 {
309  // if the new length is longer than our current,
310  // extend by the delta
311  if (newLength > bufferLength)
312  {
313  ensureCapacity(newLength - bufferLength);
314  }
315  // return a pointer to the current buffer data
316  return getData();
317 }
318 
319 
320 /**
321  * Return the length of the data in the buffer currently.
322  *
323  * @return The current length, as an Integer object.
324  */
325 // in behaviour
327 {
328  return new_integer(getBLength());
329 }
330 
331 
332 // in behaviour
334 /******************************************************************************/
335 /* Function: append to the mutable buffer */
336 /******************************************************************************/
337 {
338  RexxString *string = stringArgument(obj, OREF_positional, ARG_ONE);
339  ProtectedObject p(string);
340  // make sure we have enough room
341  ensureCapacity(string->getBLength());
342 
343  copyData(dataBLength, string->getStringData(), string->getBLength());
344  this->setBLength(this->dataBLength + string->getBLength());
345  return this;
346 }
347 
348 
350 /******************************************************************************/
351 /* Function: append to the mutable buffer */
352 /******************************************************************************/
353 {
354  // make sure we have enough room
355  ensureCapacity(blength);
356 
357  this->data->copyData(dataBLength, _data, blength);
358  this->setBLength(this->dataBLength + blength);
359  return this;
360 }
361 
362 
363 // in behaviour
365 /******************************************************************************/
366 /* Function: insert string at given position */
367 /******************************************************************************/
368 {
369  // force this into string form
370  RexxString * string = stringArgument(str, OREF_positional, ARG_ONE);
371  ProtectedObject p(string);
372 
373  // we're using optional length because 0 is valid for insert.
374  sizeB_t begin = optionalNonNegative(pos, 0, OREF_positional, ARG_TWO);
375  sizeB_t insertLength = optionalLengthArgument(len, string->getBLength(), ARG_THREE);
376 
377  codepoint_t padChar = optionalPadArgument(pad, ' ', ARG_FOUR);
378 
379  sizeB_t copyLength = Numerics::minVal(insertLength, string->getBLength());
380  sizeB_t padLength = insertLength - copyLength;
381 
382 
383  // if inserting within the current bounds, we only need to add the length
384  // if inserting beyond the end, we need to make sure we add space for the gap too
385  if (begin < dataBLength)
386  {
387  // if inserting a zero length string, this is simple!
388  if (insertLength == 0)
389  {
390  return this; /* do nothing */
391  }
392  ensureCapacity(insertLength);
393  }
394  else
395  {
396  ensureCapacity(insertLength + (begin - dataBLength));
397  }
398 
399 
400  /* create space in the buffer */
401  if (begin < dataBLength)
402  {
403  openGap(begin, insertLength, dataBLength - begin);
404  }
405  else if (begin > this->dataBLength)
406  {
407  /* pad before insertion */
408  setData(dataBLength, padChar, begin - dataBLength);
409  }
410  /* insert string contents */
411  copyData(begin, string->getStringData(), copyLength);
412  // do we need data padding?
413  if (padLength > 0)
414  {
415  setData(begin + string->getBLength(), padChar, padLength);
416  }
417  // inserting after the end? the resulting length is measured from the insertion point
418  if (begin > this->dataBLength)
419  {
420  this->setBLength(begin + insertLength);
421  }
422  else
423  {
424  // just add in the inserted length
425  this->setBLength(this->dataBLength + insertLength);
426  }
427  return this;
428 }
429 
430 
431 // in behaviour
433 /******************************************************************************/
434 /* Function: replace characters in buffer contents */
435 /******************************************************************************/
436 {
437  RexxString *string = stringArgument(str, OREF_positional, ARG_ONE);
438  ProtectedObject p(string);
439  sizeB_t begin = optionalPositionArgument(pos, 1, ARG_TWO) - 1;
440  sizeB_t replaceLength = optionalLengthArgument(len, string->getBLength(), ARG_THREE);
441 
442  codepoint_t padChar = optionalPadArgument(pad, ' ', ARG_FOUR);
443 
444  // make sure we have room for this
445  ensureCapacity(begin + replaceLength);
446 
447  // is our start position beyond the current data end?
448  if (begin > dataBLength)
449  {
450  // add padding to the gap
451  setData(dataBLength, padChar, begin - dataBLength);
452  }
453 
454  // now overlay the string data
455  copyData(begin, string->getStringData(), Numerics::minVal(replaceLength, string->getBLength()));
456  // do we need additional padding?
457  if (replaceLength > string->getBLength())
458  {
459  // pad the section after the overlay
460  setData(begin + string->getBLength(), padChar, replaceLength - string->getBLength());
461  }
462 
463  // did this add to the size?
464  if (begin + replaceLength > dataBLength)
465  {
466  //adjust upward
467  this->setBLength(begin + replaceLength);
468  }
469  return this;
470 }
471 
472 
473 /**
474  * Replace a target substring within a string with
475  * a new string value. This is similar overlay, but
476  * replacing might cause the characters following the
477  * replacement position to be shifted to the left or
478  * right.
479  *
480  * @param str The replacement string.
481  * @param pos The target position (required).
482  * @param len The target length (optional). If not specified, the
483  * length of the replacement string is used, and this
484  * is essentially an overlay operation.
485  * @param pad A padding character if padding is required. The default
486  * pad is a ' '. Padding only occurs if the replacement
487  * position is beyond the current data length.
488  *
489  * @return The target mutablebuffer object.
490  */
491 // in behaviour
493 {
494  RexxString *string = stringArgument(str, OREF_positional, ARG_ONE);
495  ProtectedObject p(string);
496  sizeB_t begin = positionArgument(pos, ARG_TWO) - 1;
497  sizeB_t newLength = string->getBLength();
498  sizeB_t replaceLength = optionalLengthArgument(len, newLength, ARG_THREE);
499 
500  codepoint_t padChar = optionalPadArgument(pad, ' ', ARG_FOUR);
501  sizeB_t finalLength;
502 
503  // if replaceLength extends beyond the end of the string
504  // then we cut it.
505  if (begin > dataBLength)
506  {
507  replaceLength = 0;
508  }
509  else if (begin + replaceLength > dataBLength)
510  {
511  replaceLength = dataBLength - begin;
512  }
513 
514  // We need to add the delta between the excised string and the inserted
515  // replacement string.
516  //
517  // If this extends beyond the end of the string, then we require space for
518  // the position + the replacement string length. Else we find the required
519  // size (may be smaller than before)
520  if (begin > dataBLength)
521  {
522  finalLength = begin - replaceLength + newLength;
523  }
524  else
525  {
526  finalLength = dataBLength - replaceLength + newLength;
527  }
528 
529  // make sure we have room for this
530  ensureCapacity(finalLength);
531 
532  // is our start position beyond the current data end?
533  // NB: Even though we've adjusted the buffer size, the dataLength is still
534  // the original entry length.
535  if (begin > dataBLength)
536  {
537  // add padding to the gap
538  setData(dataBLength, padChar, begin - dataBLength);
539  // now overlay the string data
540  copyData(begin, string->getStringData(), newLength);
541  }
542  else
543  {
544  // if the strings are of different lengths, we need to adjust the size
545  // of the gap we're copying into. Only adjust if there is a real gap
546  if (replaceLength != newLength && begin + replaceLength < dataBLength)
547  {
548  // snip out the original string
549  adjustGap(begin, replaceLength, newLength);
550  }
551  // now overlay the string data
552  copyData(begin, string->getStringData(), newLength);
553  }
554 
555  // and finally adjust the length
556  this->setBLength(finalLength);
557  // our return value is always the target mutable buffer
558  return this;
559 }
560 
561 
562 // in behaviour
564 /******************************************************************************/
565 /* Function: delete character range in buffer */
566 /******************************************************************************/
567 {
568  sizeB_t begin = positionArgument(_start, ARG_ONE) - 1;
569  sizeB_t range = optionalLengthArgument(len, /*this->data->getDataLength()*/ this->dataBLength - begin, ARG_TWO);
570 
571  // is the begin point actually within the string?
572  if (begin < dataBLength)
573  { /* got some work to do? */
574  // deleting from the middle?
575  if (begin + range < dataBLength)
576  {
577  // shift everything over
578  closeGap(begin, range, dataBLength - (begin + range));
579  this->setBLength(dataBLength - range);
580  }
581  else
582  {
583  // we're just truncating
584  this->setBLength(begin);
585  }
586  }
587  return this;
588 }
589 
590 
592 /******************************************************************************/
593 /* Function: set the size of the buffer */
594 /******************************************************************************/
595 {
596  // has a reset to zero been requested?
597  if (newsize == 0)
598  {
599  // have we increased the buffer size?
601  {
602  // reallocate the buffer
603  OrefSet(this, this->data, new_buffer(defaultSize));
604  // reset the size to the default
606  }
607  this->setBLength(0);
608  }
609  // an actual resize?
610  else if (newsize != bufferLength)
611  {
612  // reallocate the buffer
613  RexxBuffer *newBuffer = new_buffer(newsize);
614  // if we're shrinking this, it truncates.
615  this->setBLength(Numerics::minVal(dataBLength, newsize));
616  newBuffer->copyData(0, data->getData(), dataBLength);
617  // replace the old buffer
618  OrefSet(this, this->data, newBuffer);
619  // and update the size....
620  bufferLength = newsize;
621  }
622  return this;
623 }
624 
625 
626 // in behaviour
628 /******************************************************************************/
629 /* Function: set the size of the buffer */
630 /******************************************************************************/
631 {
632  size_t newsize = lengthArgument(size, ARG_ONE);
633  return this->setBufferLength(newsize);
634 }
635 
636 
638 /******************************************************************************/
639 /* Function: Handle a REQUEST('STRING') request for a mutablebuffer object */
640 /******************************************************************************/
641 {
642  return new_string(this->data->getData(), this->dataBLength);
643 }
644 
645 /**
646  * Baseclass optimization for handling request array calls.
647  *
648  * @return The string object converted to an array using default arguments.
649  */
651 {
652  // forward to the Rexx version with default arguments
653  return this->makeArrayRexx(OREF_NULL);
654 }
655 
656 /**
657  * Handle the primitive class makeString optimization. This
658  * is required because MutableBuffer implements a
659  * STRING method.
660  *
661  * @return The string value of the buffer
662  */
664 {
665  // go straight to the string handler
666  return this->makeString();
667 }
668 
669 
670 /******************************************************************************/
671 /* Arguments: String position for substr */
672 /* requested length of new string */
673 /* pad character to use, if necessary */
674 /* */
675 /* Returned: string, sub string of original. */
676 /******************************************************************************/
677 // in behaviour
679  RexxInteger *arglength,
680  RexxString *pad)
681 {
682  return StringUtil::substr(getStringData(), getBLength(), argposition, arglength, pad);
683 }
684 
685 
686 /**
687  * Perform a search for a string within the buffer.
688  *
689  * @param needle The search needle.
690  * @param pstart the starting position.
691  *
692  * @return The index of the located string. Returns 0 if no matches
693  * are found.
694  */
695 // in behaviour
697 {
698  return StringUtil::posRexx(getStringData(), getBLength(), needle, pstart, range);
699 }
700 
701 
702 /**
703  * Perform a search for the last position of a string within the
704  * buffer.
705  *
706  * @param needle The search needle.
707  * @param pstart the starting position.
708  *
709  * @return The index of the located string. Returns 0 if no matches
710  * are found.
711  */
712 // in behaviour
714 {
715  return StringUtil::lastPosRexx(getStringData(), getBLength(), needle, _start, _range);
716 }
717 
718 
719 /**
720  * Perform a caseless search for a string within the buffer.
721  *
722  * @param needle The search needle.
723  * @param pstart the starting position.
724  *
725  * @return The index of the located string. Returns 0 if no matches
726  * are found.
727  */
728 // in behaviour
730 {
731  /* force needle to a string */
732  needle = stringArgument(needle, OREF_positional, ARG_ONE);
733  ProtectedObject p(needle);
734  /* get the starting position */
735  sizeB_t _start = optionalPositionArgument(pstart, 1, ARG_TWO);
736  sizeB_t _range = optionalLengthArgument(range, getBLength() - _start + 1, ARG_THREE);
737  /* pass on to the primitive function */
738  /* and return as an integer object */
739  sizeB_t result = StringUtil::caselessPos(getStringData(), getBLength(), needle , _start - 1, _range);
740  return new_integer(result);
741 }
742 
743 
744 /**
745  * Perform a caseless search for the last position of a string
746  * within the buffer.
747  *
748  * @param needle The search needle.
749  * @param pstart the starting position.
750  *
751  * @return The index of the located string. Returns 0 if no matches
752  * are found.
753  */
754 // in behaviour
756 {
757  /* force needle to a string */
758  needle = stringArgument(needle, OREF_positional, ARG_ONE);
759  ProtectedObject p(needle);
760  /* get the starting position */
761  sizeB_t _start = optionalPositionArgument(pstart, getBLength(), ARG_TWO);
763  /* pass on to the primitive function */
764  /* and return as an integer object */
765  sizeB_t result = StringUtil::caselessLastPos(getStringData(), getBLength(), needle , _start, _range);
766  return new_integer(result);
767 }
768 
769 
770 /**
771  * Extract a single character from a string object.
772  * Returns a null string if the specified position is
773  * beyond the bounds of the string.
774  *
775  * @param positionArg
776  * The position of the target character. Must be a positive
777  * whole number.
778  *
779  * @return Returns the single character at the target position.
780  * Returns a null string if the position is beyond the end
781  * of the string.
782  */
783 // in behaviour
785 {
786  return StringUtil::subchar(getStringData(), getBLength(), positionArg);
787 }
788 
789 
790 // in behaviour
792 /******************************************************************************/
793 /* Function: Split string into an array */
794 /******************************************************************************/
795 {
797 }
798 
799 
800 // in behaviour
802 /******************************************************************************/
803 /* Function: Count occurrences of one string in another. */
804 /******************************************************************************/
805 {
806  /* force needle to a string */
807  needle = stringArgument(needle, OREF_positional, ARG_ONE);
808  ProtectedObject p(needle);
809  // delegate the counting to the string util
811 }
812 
813 // in behaviour
815 /******************************************************************************/
816 /* Function: Count occurrences of one string in another. */
817 /******************************************************************************/
818 {
819  /* force needle to a string */
820  needle = stringArgument(needle, OREF_positional, ARG_ONE);
821  ProtectedObject p(needle);
822  // delegate the counting to the string util
824 }
825 
826 /**
827  * Do an inplace changeStr operation on a mutablebuffer.
828  *
829  * @param needle The search needle.
830  * @param newNeedle The replacement string.
831  * @param countArg The number of occurrences to replace.
832  *
833  * @return The target MutableBuffer
834  */
835 // in behaviour
837 {
838  /* force needle to a string */
839  needle = stringArgument(needle, OREF_positional, ARG_ONE);
840  ProtectedObject p1(needle);
841  /* newneedle must be a string two */
842  newNeedle = stringArgument(newNeedle, OREF_positional, ARG_TWO);
843  ProtectedObject p2(newNeedle);
844 
845  // we'll only change up to a specified count. If not there, we do everything.
846  size_t count = optionalPositive(countArg, Numerics::MAX_WHOLENUMBER, OREF_positional, ARG_THREE);
847  // find the number of matches in the string
848  size_t matches = StringUtil::countStr(getStringData(), getBLength(), needle);
849  if (matches > count) // the matches are bounded by the count
850  {
851  matches = count;
852  }
853  // no matches is easy!
854  if (matches == 0)
855  {
856  return this;
857  }
858  sizeB_t needleLength = needle->getBLength(); /* get the length of the needle */
859  sizeB_t newLength = newNeedle->getBLength(); /* and the replacement length */
860  // calculate the final length and make sure we have enough space
861  sizeB_t resultLength = this->getBLength() - (matches * needleLength) + (matches * newLength);
862  ensureCapacity(resultLength);
863 
864  // an inplace update has complications, depending on whether the new string is shorter,
865  // the same length, or longer
866 
867  // simplest case...same length strings. We can just overlay the existing occurrences
868  if (needleLength == newLength)
869  {
870  const char *source = getStringData();
871  sizeB_t sourceLength = getBLength();
872  sizeB_t _start = 0; /* set a zero starting point */
873  for (size_t i = 0; i < matches; i++)
874  {
875  // search for the next occurrence...which should be there because we
876  // already know the count
877  sizeB_t matchPos = StringUtil::pos(source, sourceLength, needle, _start, sourceLength);
878  copyData(matchPos - 1, newNeedle->getStringData(), newLength);
879  // step to the next search position
880  _start = matchPos + newLength - 1;
881  }
882  }
883  // this will be a shorter thing, so we can do things in place as if we were using two buffers
884  else if (needleLength > newLength)
885  {
886  // we start building from the beginning
887  sizeB_t copyOffset = 0;
888  sizeB_t _start = 0;
889  // get our string bounds
890  const char *source = getStringData();
891  sizeB_t sourceLength = getBLength();
892  const char *newPtr = newNeedle->getStringData();
893  // this is our scan offset
894  for (size_t i = 0; i < matches; i++)
895  {
896  // look for each instance and replace
897  sizeB_t matchPos = StringUtil::pos(source, sourceLength, needle, _start, sourceLength);
898  sizeB_t copyLength = (matchPos - 1) - _start; /* get the next length to copy */
899  // if this skipped over characters, we need to copy those
900  if (copyLength != 0)
901  {
902  copyData(copyOffset, source + _start, copyLength);
903  copyOffset += copyLength;
904  }
905  // replacing with a non-null string, copy the replacement string in
906  if (newLength != 0)
907  {
908  copyData(copyOffset, newPtr, newLength);
909  copyOffset += newLength;
910  }
911  _start = matchPos + needleLength - 1; /* step to the next position */
912  }
913  // we likely have some remainder that needs copying
914  if (_start < sourceLength)
915  {
916  copyData(copyOffset, source + _start, sourceLength - _start);
917  }
918  }
919  // hardest case...the string gets longer. We need to shift all of the data
920  // to the end and then pull the pieces back in as we go
921  else
922  {
923  sizeB_t growth = (newLength - needleLength) * matches;
924 
925  // we start building from the beginning
926  sizeB_t copyOffset = 0;
927  sizeB_t _start = 0;
928  // get our string bounds
929  const char *source = getStringData() + growth;
930  sizeB_t sourceLength = getBLength();
931  // this shifts everything to the end of the buffer. From there,
932  // we pull pieces back into place.
933  openGap(0, growth, sourceLength);
934  const char *newPtr = newNeedle->getStringData();
935  // this is our scan offset
936  for (size_t i = 0; i < matches; i++)
937  {
938  // look for each instance and replace
939  sizeB_t matchPos = StringUtil::pos(source, sourceLength, needle, _start, sourceLength);
940  sizeB_t copyLength = (matchPos - 1) - _start; /* get the next length to copy */
941  // if this skipped over characters, we need to copy those
942  if (copyLength != 0)
943  {
944  copyData(copyOffset, source + _start, copyLength);
945  copyOffset += copyLength;
946  }
947  // replacing with a non-null string, copy the replacement string in
948  if (newLength != 0)
949  {
950  copyData(copyOffset, newPtr, newLength);
951  copyOffset += newLength;
952  }
953  _start = matchPos + needleLength - 1; /* step to the next position */
954  }
955  // we likely have some remainder that needs copying
956  if (_start < sourceLength)
957  {
958  copyData(copyOffset, source + _start, sourceLength - _start);
959  }
960  }
961  // update the result length, and return
962  this->setBLength(resultLength);
963  return this;
964 }
965 
966 /**
967  * Do an inplace caseless changeStr operation on a
968  * mutablebuffer.
969  *
970  * @param needle The search needle.
971  * @param newNeedle The replacement string.
972  * @param countArg The number of occurrences to replace.
973  *
974  * @return The target MutableBuffer
975  */
976 // in beahviour
978 {
979  /* force needle to a string */
980  needle = stringArgument(needle, OREF_positional, ARG_ONE);
981  ProtectedObject p1(needle);
982  /* newneedle must be a string two */
983  newNeedle = stringArgument(newNeedle, OREF_positional, ARG_TWO);
984  ProtectedObject p2(newNeedle);
985 
986  // we'll only change up to a specified count. If not there, we do everything.
987  size_t count = optionalPositive(countArg, Numerics::MAX_WHOLENUMBER, OREF_positional, ARG_THREE);
988  // find the number of matches in the string
989  size_t matches = StringUtil::caselessCountStr(getStringData(), getBLength(), needle);
990  if (matches > count) // the matches are bounded by the count
991  {
992  matches = count;
993  }
994  // no matches is easy!
995  if (matches == 0)
996  {
997  return this;
998  }
999  sizeB_t needleLength = needle->getBLength(); /* get the length of the needle */
1000  sizeB_t newLength = newNeedle->getBLength(); /* and the replacement length */
1001  // calculate the final length and make sure we have enough space
1002  sizeB_t resultLength = this->getBLength() - (matches * needleLength) + (matches * newLength);
1003  ensureCapacity(resultLength);
1004 
1005  // an inplace update has complications, depending on whether the new string is shorter,
1006  // the same length, or longer
1007 
1008  // simplest case...same length strings. We can just overlay the existing occurrences
1009  if (needleLength == newLength)
1010  {
1011  const char *source = getStringData();
1012  sizeB_t sourceLength = getBLength();
1013  sizeB_t _start = 0; /* set a zero starting point */
1014  for (size_t i = 0; i < matches; i++)
1015  {
1016  // search for the next occurrence...which should be there because we
1017  // already know the count
1018  sizeB_t matchPos = StringUtil::caselessPos(source, sourceLength, needle, _start, sourceLength);
1019  copyData(matchPos - 1, newNeedle->getStringData(), newLength);
1020  // step to the next search position
1021  _start = matchPos + newLength - 1;
1022  }
1023  }
1024  // this will be a shorter thing, so we can do things in place as if we were using two buffers
1025  else if (needleLength > newLength)
1026  {
1027  // we start building from the beginning
1028  sizeB_t copyOffset = 0;
1029  sizeB_t _start = 0;
1030  // get our string bounds
1031  const char *source = getStringData();
1032  sizeB_t sourceLength = getBLength();
1033  const char *newPtr = newNeedle->getStringData();
1034  // this is our scan offset
1035  for (size_t i = 0; i < matches; i++)
1036  {
1037  // look for each instance and replace
1038  sizeB_t matchPos = StringUtil::caselessPos(source, sourceLength, needle, _start, sourceLength);
1039  sizeB_t copyLength = (matchPos - 1) - _start; /* get the next length to copy */
1040  // if this skipped over characters, we need to copy those
1041  if (copyLength != 0)
1042  {
1043  copyData(copyOffset, source + _start, copyLength);
1044  copyOffset += copyLength;
1045  }
1046  // replacing with a non-null string, copy the replacement string in
1047  if (newLength != 0)
1048  {
1049  copyData(copyOffset, newPtr, newLength);
1050  copyOffset += newLength;
1051  }
1052  _start = matchPos + needleLength - 1; /* step to the next position */
1053  }
1054  // we likely have some remainder that needs copying
1055  if (_start < sourceLength)
1056  {
1057  copyData(copyOffset, source + _start, sourceLength - _start);
1058  }
1059  }
1060  // hardest case...the string gets longer. We need to shift all of the data
1061  // to the end and then pull the pieces back in as we go
1062  else
1063  {
1064  sizeB_t growth = (newLength - needleLength) * matches;
1065 
1066  // we start building from the beginning
1067  sizeB_t copyOffset = 0;
1068  sizeB_t _start = 0;
1069  // get our string bounds
1070  const char *source = getStringData() + growth;
1071  sizeB_t sourceLength = getBLength();
1072  // this shifts everything to the end of the buffer. From there,
1073  // we pull pieces back into place.
1074  openGap(0, growth, sourceLength);
1075  const char *newPtr = newNeedle->getStringData();
1076  // this is our scan offset
1077  for (size_t i = 0; i < matches; i++)
1078  {
1079  // look for each instance and replace
1080  sizeB_t matchPos = StringUtil::caselessPos(source, sourceLength, needle, _start, sourceLength);
1081  sizeB_t copyLength = (matchPos - 1) - _start; /* get the next length to copy */
1082  // if this skipped over characters, we need to copy those
1083  if (copyLength != 0)
1084  {
1085  copyData(copyOffset, source + _start, copyLength);
1086  copyOffset += copyLength;
1087  }
1088  // replacing with a non-null string, copy the replacement string in
1089  if (newLength != 0)
1090  {
1091  copyData(copyOffset, newPtr, newLength);
1092  copyOffset += newLength;
1093  }
1094  _start = matchPos + needleLength - 1; /* step to the next position */
1095  }
1096  // we likely have some remainder that needs copying
1097  if (_start < sourceLength)
1098  {
1099  copyData(copyOffset, source + _start, sourceLength - _start);
1100  }
1101  }
1102  // update the result length, and return
1103  this->setBLength(resultLength);
1104  return this;
1105 }
1106 
1107 
1108 /**
1109  * Rexx exported method stub for the lower() method.
1110  *
1111  * @param start The optional starting location. Defaults to the first character
1112  * if not specified.
1113  * @param length The length to convert. Defaults to the segment from the start
1114  * position to the end of the string.
1115  *
1116  * @return A new string object with the case conversion applied.
1117  */
1118 // in behaviour
1120 {
1121  sizeB_t startPos = optionalPositionArgument(_start, 1, ARG_ONE) - 1;
1122  sizeB_t rangeLength = optionalLengthArgument(_length, getBLength(), ARG_TWO);
1123 
1124  // if we're starting beyond the end bounds, return unchanged
1125  if (startPos >= getBLength())
1126  {
1127  return this;
1128  }
1129 
1130  rangeLength = Numerics::minVal(rangeLength, getBLength() - startPos);
1131 
1132  // a zero length value is also a non-change.
1133  if (rangeLength == 0)
1134  {
1135  return this;
1136  }
1137 
1138  char *bufferData = getData() + startPos;
1139  // now uppercase in place
1140  for (size_t i = 0; i < rangeLength; i++)
1141  {
1142  *bufferData = tolower(*bufferData);
1143  bufferData++;
1144  }
1145  return this;
1146 }
1147 
1148 
1149 /**
1150  * Rexx exported method stub for the upper() method.
1151  *
1152  * @param start The optional starting location. Defaults to the first character
1153  * if not specified.
1154  * @param length The length to convert. Defaults to the segment from the start
1155  * position to the end of the string.
1156  *
1157  * @return A new string object with the case conversion applied.
1158  */
1159 // in beahviour
1161 {
1162  sizeB_t startPos = optionalPositionArgument(_start, 1, ARG_ONE) - 1;
1163  sizeB_t rangeLength = optionalLengthArgument(_length, getBLength(), ARG_TWO);
1164 
1165  // if we're starting beyond the end bounds, return unchanged
1166  if (startPos >= getBLength())
1167  {
1168  return this;
1169  }
1170 
1171  rangeLength = Numerics::minVal(rangeLength, getBLength() - startPos);
1172 
1173  // a zero length value is also a non-change.
1174  if (rangeLength == 0)
1175  {
1176  return this;
1177  }
1178 
1179  char *bufferData = getData() + startPos;
1180  // now uppercase in place
1181  for (size_t i = 0; i < rangeLength; i++)
1182  {
1183  *bufferData = toupper(*bufferData);
1184  bufferData++;
1185  }
1186  return this;
1187 }
1188 
1189 
1190 /**
1191  * translate characters in the buffer using a translation table.
1192  *
1193  * @param tableo The output table specification
1194  * @param tablei The input table specification
1195  * @param pad An optional padding character (default is a space).
1196  * @param _start The starting position to translate.
1197  * @param _range The length to translate
1198  *
1199  * @return The target mutable buffer.
1200  */
1201 // in behaviour
1203 {
1204  // just a simple uppercase?
1205  if (tableo == OREF_NULL && tablei == OREF_NULL && pad == OREF_NULL)
1206  {
1207  return this->upper(_start, _range);
1208  }
1209  /* validate the tables */
1210  tableo = optionalStringArgument(tableo, OREF_positional, OREF_NULLSTRING, ARG_ONE);
1211  ProtectedObject p1(tableo);
1212  sizeB_t outTableLength = tableo->getBLength(); /* get the table length */
1213  /* input table too */
1214  tablei = optionalStringArgument(tablei, OREF_positional, OREF_NULLSTRING, ARG_TWO);
1215  ProtectedObject p2(tablei);
1216  sizeB_t inTableLength = tablei->getBLength(); /* get the table length */
1217  const char *inTable = tablei->getStringData(); /* point at the input table */
1218  const char *outTable = tableo->getStringData(); /* and the output table */
1219  /* get the pad character */
1220  codepoint_t padChar = optionalPadArgument(pad, ' ', ARG_THREE);
1221  sizeB_t startPos = optionalPositionArgument(_start, 1, ARG_FOUR);
1222  sizeB_t range = optionalLengthArgument(_range, getBLength() - startPos + 1, ARG_FOUR);
1223 
1224  // if nothing to translate, we can return now
1225  if (startPos > getBLength() || range == 0)
1226  {
1227  return this;
1228  }
1229  // cape the real range
1230  range = Numerics::minVal(range, getBLength() - startPos + 1);
1231  char *scanPtr = getData() + startPos - 1; /* point to data */
1232  sizeB_t scanLength = range; /* get the length too */
1233 
1234  while (scanLength-- != 0)
1235  { /* spin thru input */
1236  char ch = *scanPtr; /* get a character */
1237  size_t position;
1238 
1239  if (tablei != OREF_NULLSTRING) /* input table specified? */
1240  {
1241  /* search for the character */
1242  position = StringUtil::memPos(inTable, inTableLength, ch);
1243  }
1244  else
1245  {
1246  position = ((size_t)ch) & 0xff; /* position is the character value */
1247  }
1248  if (position != (size_t)(-1))
1249  { /* found in the table? */
1250  if (position < outTableLength) /* in the output table? */
1251  {
1252  /* convert the character */
1253  *scanPtr = *(outTable + position);
1254  }
1255  else
1256  {
1257  *scanPtr = padChar; /* else use the pad character */
1258  }
1259  }
1260  scanPtr++; /* step the pointer */
1261  }
1262  return this;
1263 }
1264 
1265 
1266 /**
1267  * Test if regions within two strings match.
1268  *
1269  * @param start_ The starting compare position within the target string. This
1270  * must be within the bounds of the string.
1271  * @param other The other compare string.
1272  * @param offset_ The starting offset of the compare string. This must be
1273  * within the string bounds. The default start postion is 1.
1274  * @param len_ The length of the compare substring. The length and the
1275  * offset must specify a valid substring of other. If not
1276  * specified, this defaults to the substring from the
1277  * offset to the end of the string.
1278  *
1279  * @return True if the two regions match, false for any mismatch.
1280  */
1281 // in behaviour
1283 {
1284  stringsizeB_t _start = positionArgument(start_, ARG_ONE);
1285  // the start position must be within the string bounds
1286  if (_start > getBLength())
1287  {
1289  }
1290  other = stringArgument(other, OREF_positional, ARG_TWO);
1291 
1292  stringsizeB_t offset = optionalPositionArgument(offset_, 1, ARG_THREE);
1293 
1294  if (offset > other->getBLength())
1295  {
1297  }
1298 
1299  stringsizeB_t len = optionalLengthArgument(len_, other->getBLength() - offset + 1, ARG_FOUR);
1300 
1301  if ((offset + len - 1) > other->getBLength())
1302  {
1304  }
1305 
1306  return primitiveMatch(_start, other, offset, len) ? TheTrueObject : TheFalseObject;
1307 }
1308 
1309 
1310 /**
1311  * Test if regions within two strings match.
1312  *
1313  * @param start_ The starting compare position within the target string. This
1314  * must be within the bounds of the string.
1315  * @param other The other compare string.
1316  * @param offset_ The starting offset of the compare string. This must be
1317  * within the string bounds. The default start postion is 1.
1318  * @param len_ The length of the compare substring. The length and the
1319  * offset must specify a valid substring of other. If not
1320  * specified, this defaults to the substring from the
1321  * offset to the end of the string.
1322  *
1323  * @return True if the two regions match, false for any mismatch.
1324  */
1325 // in behaviour
1327 {
1328  stringsizeB_t _start = positionArgument(start_, ARG_ONE);
1329  // the start position must be within the string bounds
1330  if (_start > getBLength())
1331  {
1333  }
1334  other = stringArgument(other, OREF_positional, ARG_TWO);
1335 
1336  stringsizeB_t offset = optionalPositionArgument(offset_, 1, ARG_THREE);
1337 
1338  if (offset > other->getBLength())
1339  {
1341  }
1342 
1343  stringsizeB_t len = optionalLengthArgument(len_, other->getBLength() - offset + 1, ARG_FOUR);
1344 
1345  if ((offset + len - 1) > other->getBLength())
1346  {
1348  }
1349 
1350  return primitiveCaselessMatch(_start, other, offset, len) ? TheTrueObject : TheFalseObject;
1351 }
1352 
1353 
1354 /**
1355  * Perform a compare of regions of two string objects. Returns
1356  * true if the two regions match, returns false for mismatches.
1357  *
1358  * @param start The starting offset within the target string.
1359  * @param other The source string for the compare.
1360  * @param offset The offset of the substring of the other string to use.
1361  * @param len The length of the substring to compare.
1362  *
1363  * @return True if the regions match, false otherwise.
1364  */
1366 {
1367  _start--; // make the starting point origin zero
1368  offset--;
1369 
1370  // if the match is not possible in the target string, just return false now.
1371  if ((_start + len) > getBLength())
1372  {
1373  return false;
1374  }
1375 
1376  return memcmp(getStringData() + _start, other->getStringData() + offset, len) == 0;
1377 }
1378 
1379 
1380 /**
1381  * Perform a caselesee compare of regions of two string objects.
1382  * Returns true if the two regions match, returns false for
1383  * mismatches.
1384  *
1385  * @param start The starting offset within the target string.
1386  * @param other The source string for the compare.
1387  * @param offset The offset of the substring of the other string to use.
1388  * @param len The length of the substring to compare.
1389  *
1390  * @return True if the regions match, false otherwise.
1391  */
1393 {
1394  _start--; // make the starting point origin zero
1395  offset--;
1396 
1397  // if the match is not possible in the target string, just return false now.
1398  if ((_start + len) > getBLength())
1399  {
1400  return false;
1401  }
1402 
1403  return StringUtil::caselessCompare(getStringData() + _start, other->getStringData() + offset, len) == 0;
1404 }
1405 
1406 
1407 /**
1408  * Compare a single character at a give position against
1409  * a set of characters to see if any of the characters is
1410  * a match.
1411  *
1412  * @param position_ The character position
1413  * @param matchSet The set to compare against.
1414  *
1415  * @return true if the character at the give position is any of the characters,
1416  * false if none of them match.
1417  */
1418 // in behaviour
1420 {
1421  stringsizeB_t position = positionArgument(position_, ARG_ONE);
1422  // the start position must be within the string bounds
1423  if (position > getBLength())
1424  {
1426  }
1427  matchSet = stringArgument(matchSet, OREF_positional, ARG_TWO);
1428 
1429  stringsizeB_t _setLength = matchSet->getBLength();
1430  codepoint_t _matchChar = getCharB(position - 1);
1431 
1432  // iterate through the match set looking for a match
1433  for (stringsizeB_t i = 0; i < _setLength; i++)
1434  {
1435  if (_matchChar == matchSet->getCharB(i))
1436  {
1437  return TheTrueObject;
1438  }
1439  }
1440  return TheFalseObject;
1441 }
1442 
1443 
1444 /**
1445  * Compare a single character at a give position against
1446  * a set of characters to see if any of the characters is
1447  * a match.
1448  *
1449  * @param position_ The character position
1450  * @param matchSet The set to compare against.
1451  *
1452  * @return true if the character at the give position is any of the characters,
1453  * false if none of them match.
1454  */
1455 // in behaviour
1457 {
1458  stringsizeB_t position = positionArgument(position_, ARG_ONE);
1459  // the start position must be within the string bounds
1460  if (position > getBLength())
1461  {
1463  }
1464  matchSet = stringArgument(matchSet, OREF_positional, ARG_TWO);
1465 
1466  stringsizeB_t _setLength = matchSet->getBLength();
1467  codepoint_t _matchChar = getCharB(position - 1);
1468  _matchChar = toupper(_matchChar);
1469 
1470  // iterate through the match set looking for a match, using a
1471  // caseless compare
1472  for (stringsizeB_t i = 0; i < _setLength; i++)
1473  {
1474  if (_matchChar == toupper(matchSet->getCharB(i)))
1475  {
1476  return TheTrueObject;
1477  }
1478  }
1479  return TheFalseObject;
1480 }
1481 
1482 
1483 /**
1484  * Perform a character verify operation on a mutable buffer.
1485  *
1486  * @param ref The reference string.
1487  * @param option The match/nomatch option.
1488  * @param _start The start position for the verify.
1489  * @param range The range to search
1490  *
1491  * @return The offset of the first match/mismatch within the buffer.
1492  */
1493 // in behaviour
1495 {
1496  return StringUtil::verify(getStringData(), getBLength(), ref, option, _start, range);
1497 }
1498 
1499 
1500 /**
1501  * Perform a subword extraction from a mutable buffer.
1502  *
1503  * @param position The first word to be extracted.
1504  * @param plength The number of words to extract.
1505  *
1506  * @return The substring containing the extacted words.
1507  */
1508 // in behaviour
1510 {
1511  return StringUtil::subWord(getStringData(), getBLength(), position, plength);
1512 }
1513 
1514 
1515 /**
1516  * Returns an array of all words contained in the given range
1517  * of the string, using the same extraction rules used
1518  * for subWord() and word().
1519  *
1520  * @param position The optional starting position. If not provided, extraction
1521  * starts with the first word.
1522  * @param plength The number of words to extract. If omitted, will extract
1523  * from the starting postion to the end of the string.
1524  *
1525  * @return An array containing the extracted words. If no words are
1526  * available within the given range, this returns an empty
1527  * array.
1528  */
1530 {
1531  return StringUtil::subWords(getStringData(), getBLength(), position, plength);
1532 }
1533 
1534 
1535 /**
1536  * Extract a given word from a mutable buffer.
1537  *
1538  * @param position The target word position.
1539  *
1540  * @return The extracted word, as a string.
1541  */
1542 // in behaviour
1544 {
1545  return StringUtil::word(getStringData(), getBLength(), position);
1546 }
1547 
1548 
1549 /**
1550  * return the index of a given word position in a mutable buffer
1551  *
1552  *
1553  * @param position The target word position.
1554  *
1555  * @return The position of the target word.
1556  */
1557 // in behaviour
1559 {
1560  return StringUtil::wordIndex(getStringData(), getBLength(), position);
1561 }
1562 
1563 
1564 /**
1565  * return the length of a given word position in a mutable
1566  * buffer
1567  *
1568  *
1569  * @param position The target word position.
1570  *
1571  * @return The length of the target word.
1572  */
1573 // in behaviour
1575 {
1576  return StringUtil::wordLength(getStringData(), getBLength(), position);
1577 }
1578 
1579 /**
1580  * Return the count of words in the buffer.
1581  *
1582  * @return The buffer word count.
1583  */
1584 // in behaviour
1586 {
1587  size_t tempCount = StringUtil::wordCount(this->getStringData(), this->getBLength());
1588  return new_integer(tempCount);
1589 }
1590 
1591 
1592 /**
1593  * Perform a wordpos search on a mutablebuffer object.
1594  *
1595  * @param phrase The search phrase
1596  * @param pstart The starting search position.
1597  *
1598  * @return The index of the match location.
1599  */
1600 // in behaviour
1602 {
1603  return StringUtil::wordPos(getStringData(), getBLength(), phrase, pstart);
1604 }
1605 
1606 
1607 /**
1608  * Perform a caseless wordpos search on a string object.
1609  *
1610  * @param phrase The search phrase
1611  * @param pstart The starting search position.
1612  *
1613  * @return The index of the match location.
1614  */
1615 // in behaviour
1617 {
1618  return StringUtil::caselessWordPos(getStringData(), getBLength(), phrase, pstart);
1619 }
1620 
1621 
1622 /**
1623  * Perform a delword operation on a mutable buffer
1624  *
1625  * @param position The position to delete.
1626  * @param plength The number of words to delete
1627  *
1628  * @return Always returns the target mutable buffer.
1629  */
1630 // in behaviour
1632 {
1633  /* convert position to binary */
1634  sizeB_t _wordPos = positionArgument(position, ARG_ONE);
1635  /* get num of words to delete, the */
1636  /* default is "a very large number" */
1637  size_t count = optionalLengthArgument(plength, Numerics::MAX_WHOLENUMBER, ARG_TWO);
1638 
1639  sizeB_t length = getBLength(); /* get string length */
1640  if (length == 0) /* null string? */
1641  {
1642  return this; /* nothing to delete */
1643  }
1644  if (count == 0) /* deleting zero words? */
1645  {
1646  return this; /* also very easy */
1647  }
1648  const char *_word = getStringData(); /* point to the string */
1649  const char *nextSite = NULL;
1650  /* get the first word */
1651  sizeB_t _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1652  while (--_wordPos > 0 && _wordLength != 0)
1653  { /* loop until we reach tArget */
1654  _word = nextSite; /* copy the start pointer */
1655  /* get the next word */
1656  _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1657  }
1658  if (_wordPos != 0) /* run out of words first */
1659  {
1660  return this; /* return the buffer unaltered */
1661  }
1662  // get the deletion point as an offset
1663  sizeB_t deletePosition = sizeB_v(_word - this->getStringData());
1664  while (--count > 0 && _wordLength != 0)
1665  { /* loop until we reach tArget */
1666  _word = nextSite; /* copy the start pointer */
1667  /* get the next word */
1668  _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1669  }
1670  if (length != 0) /* didn't use up the string */
1671  {
1672  StringUtil::skipBlanks(&nextSite, &length);/* skip over trailing blanks */
1673  }
1674 
1675  sizeB_t gapSize = dataBLength - (deletePosition + length);
1676  // close up the delete part
1677  closeGap(deletePosition, gapSize, length);
1678  // adjust for the deleted data
1679  this->setBLength(dataBLength - gapSize);
1680  return this;
1681 }
1682 
1683 
1684 /**
1685 * Do an inplace space() operation on a mutable buffer.
1686 *
1687 * @param space_count The number of pad characters between
1688 * each word
1689 * @param pad The pad character
1690 *
1691 * @return The target MutableBuffer
1692 */
1694 {
1695  size_t count = 0; /* count word interstices in buffer*/
1696 
1697  /* get the spacing count */
1698  const size_t padLength = optionalLengthArgument(space_count, 1, ARG_ONE);
1699  /* get the pad character */
1700  const char padChar = optionalPadArgument(pad, ' ', ARG_TWO);
1701 
1702  // an inplace update has complications, depending on whether the new string
1703  // is shorter or longer than the original.
1704  // first execute padC with padLength == 0,1; later expand padC to padLength
1705  const char padC = ' '; /* intermediate pad: single space */
1706  const sizeB_t padL = 1; /* intermediate pad length: 1 */
1707 
1708  // With padC the new string is not longer, so we can just overlay in place.
1709  // Set write position to start of buffer
1710  // Find first word: start position and length
1711  // While a word is found:
1712  // Copy word to write position
1713  // update write position
1714  // Find next word: start position and length
1715  // if no next word exists then leave
1716  // select spacing count:
1717  // when = 1 then append padChar and update write position
1718  // when = 0 then don't pad
1719  // otherwise append padC and update write position
1720  // increment word interstice count
1721  // iterate
1722  // adjust string dataLength to write position
1723  sizeB_t writePos = 0; /* offset current write position */
1724  const char *_word = getStringData(); /* point to the start of string */
1725  const char *nextSite = NULL; /* start of the next word */
1726  sizeB_t length = getBLength(); /* get string data length */
1727 
1728  /* get the first word */
1729  sizeB_t _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1730 
1731  while (_wordLength != 0)
1732  {
1733  /* copy first word to writePos */
1734  copyData(writePos, _word, _wordLength);
1735  writePos += _wordLength; /* update writePos for next word */
1736  _word = nextSite; /* set start pointer to next word */
1737  /* get the next word */
1738  _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1739  if (_wordLength == 0) /* is there no next word coming ? */
1740  {
1741  break; /* don't pad or count last word */
1742  }
1743  switch (padLength) /* handle different padLength */
1744  {
1745  case 1: /* more frequent case goes first */
1746  setData(writePos, padChar, padLength); /* write pad character */
1747  writePos += padLength; /* move write position one byte */
1748  break;
1749  case 0:
1750  break; /* don't write pad character */
1751  default: /* padLength > 1 */
1752  setData(writePos, padC, padL); /* write padC pad character */
1753  writePos += padL; /* move write position one byte */
1754  }
1755  count++; /* increment the word count */
1756  }
1757  this->dataBLength = writePos; /* set data length in buffer */
1758 
1759  if ( padLength > 1 ) /* do we need to expand padC ? */
1760  {
1761  sizeB_t growth = count * (padLength-1); /* data grows by so many bytes */
1762  ensureCapacity(growth); /* make sure we have room for this */
1763 
1764  // As the string gets longer, we need to shift all data to the end and
1765  // then pull the pieces back in as we go.
1766  length = getBLength(); /* get current string data length */
1767  openGap(0, growth, length); /* shift towards end of the buffer */
1768  writePos = 0;
1769  while (growth>0)
1770  {
1771  setData(writePos, padC, padL); /* fill gap with whitespace */
1772  writePos++;
1773  growth--;
1774  }
1775  dataBLength = getBLength() + count * (padLength-1);/*adjust data to size*/
1776 
1777  // Now we do the last loop over, using padChar and padLength
1778  writePos = 0; /* offset current write position */
1779  const char *_word = getStringData(); /*point to the start of string*/
1780  const char *nextSite = NULL; /* start of the next word */
1781  length = this->dataBLength; /* get current string data length */
1782  /* get the first word */
1783  _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1784 
1785  while (_wordLength != 0) /* while there is a word ... */
1786  {
1787  /* copy first word to writePos */
1788  copyData(writePos, _word, _wordLength);
1789  writePos += _wordLength; /* update writePos for next word */
1790  _word = nextSite; /* set start pointer to next word */
1791  /* get the next word */
1792  _wordLength = StringUtil::nextWord(&_word, &length, &nextSite);
1793  if (_wordLength != 0) /* except for the last word */
1794  {
1795  setData(writePos, padChar, padLength); /* write padChar chars */
1796  writePos += padLength; /* update writePos for next word */
1797  }
1798  }
1799  }
1800  return this; /* return the mutable buffer */
1801 }
void reportException(wholenumber_t error)
RexxBuffer * new_buffer(sizeB_t s)
@ T_MutableBuffer
RexxInteger * new_integer(wholenumber_t v)
#define DEFAULT_BUFFER_LENGTH
codepoint_t optionalPadArgument(RexxObject *o, codepoint_t d, size_t p)
Definition: RexxCore.h:382
#define OREF_NULL
Definition: RexxCore.h:60
RexxString * stringArgument(RexxObject *object, RexxString *kind, size_t position)
Definition: RexxCore.h:303
const int ARG_FOUR
Definition: RexxCore.h:83
const int ARG_THREE
Definition: RexxCore.h:82
size_t optionalPositive(RexxObject *o, size_t d, RexxString *kind, size_t p)
Definition: RexxCore.h:399
#define OrefSet(o, r, v)
Definition: RexxCore.h:94
#define TheTrueObject
Definition: RexxCore.h:186
const int ARG_TWO
Definition: RexxCore.h:81
size_t optionalNonNegative(RexxObject *o, size_t d, RexxString *kind, size_t p)
Definition: RexxCore.h:394
size_t optionalLengthArgument(RexxObject *o, size_t d, size_t p)
Definition: RexxCore.h:343
size_t optionalPositionArgument(RexxObject *o, size_t d, size_t p)
Definition: RexxCore.h:363
#define TheFalseObject
Definition: RexxCore.h:185
const int ARG_ONE
Definition: RexxCore.h:80
RexxString * optionalStringArgument(RexxObject *o, RexxString *d, RexxString *kind, size_t p)
Definition: RexxCore.h:328
#define Error_Incorrect_method_position
#define Error_Incorrect_method_length
#define memory_mark(oref)
Definition: RexxMemory.hpp:445
RexxObject * new_object(size_t s)
Definition: RexxMemory.hpp:431
#define flatten_reference(oref, envel)
Definition: RexxMemory.hpp:493
#define CLASS_CREATE(name, id, className)
Definition: RexxMemory.hpp:498
#define memory_mark_general(oref)
Definition: RexxMemory.hpp:446
#define cleanUpFlatten
Definition: RexxMemory.hpp:479
#define setUpFlatten(type)
Definition: RexxMemory.hpp:473
RexxString * new_string(const char *s, stringsizeB_t bl, sizeC_t cl=-1)
stringsize_t positionArgument(RexxObject *argument, size_t position)
stringsize_t lengthArgument(RexxObject *argument, size_t position)
static const wholenumber_t MAX_WHOLENUMBER
Definition: Numerics.hpp:62
static wholenumber_t minVal(wholenumber_t n1, wholenumber_t n2)
Definition: Numerics.hpp:116
void setDataLength(sizeB_t l)
Definition: BufferClass.hpp:55
void copyData(sizeB_t offset, const char *string, sizeB_t l)
Definition: BufferClass.hpp:57
sizeB_t getDataLength()
Definition: BufferClass.hpp:53
virtual char * getData()
void setBehaviour(RexxBehaviour *b)
RexxObject * clone()
RexxMutableBuffer * newRexx(RexxObject **, size_t, size_t)
RexxMutableBuffer * appendCstring(const char *, sizeB_t blength)
RexxInteger * caselessLastPos(RexxString *needle, RexxInteger *_start, RexxInteger *_range)
RexxMutableBuffer * caselessChangeStr(RexxString *needle, RexxString *newNeedle, RexxInteger *countArg)
RexxMutableBuffer * translate(RexxString *tableo, RexxString *tablei, RexxString *pad, RexxInteger *, RexxInteger *)
bool primitiveMatch(stringsizeB_t start, RexxString *other, stringsizeB_t offset, stringsizeB_t len)
void closeGap(sizeB_t offset, sizeB_t _size, sizeB_t tailSize)
RexxString * subchar(RexxInteger *startPosition)
char getCharB(sizeB_t offset)
RexxMutableBuffer * mydelete(RexxObject *, RexxObject *)
RexxArray * subWords(RexxInteger *, RexxInteger *)
RexxMutableBuffer * changeStr(RexxString *needle, RexxString *newNeedle, RexxInteger *countArg)
void liveGeneral(int reason)
sizeB_t setDataLength(sizeB_t l)
void adjustGap(sizeB_t offset, sizeB_t _size, sizeB_t _newSize)
void openGap(sizeB_t offset, sizeB_t _size, sizeB_t tailSize)
RexxInteger * verify(RexxString *, RexxString *, RexxInteger *, RexxInteger *)
RexxMutableBuffer * lower(RexxInteger *_start, RexxInteger *_length)
static void createInstance()
void flatten(RexxEnvelope *envelope)
const char * getStringData()
RexxMutableBuffer * append(RexxObject *)
void setBLength(sizeB_t l)
RexxInteger * posRexx(RexxString *needle, RexxInteger *_start, RexxInteger *_range)
RexxMutableBuffer * space(RexxInteger *space_count, RexxString *pad)
RexxMutableBuffer * delWord(RexxInteger *position, RexxInteger *plength)
RexxInteger * wordPos(RexxString *, RexxInteger *)
void setData(sizeB_t offset, char character, sizeB_t l)
bool primitiveCaselessMatch(stringsizeB_t start, RexxString *other, stringsizeB_t offset, stringsizeB_t len)
RexxInteger * caselessWordPos(RexxString *, RexxInteger *)
void ensureCapacity(sizeB_t addedLength)
RexxInteger * caselessMatch(RexxInteger *start_, RexxString *other, RexxInteger *offset_, RexxInteger *len_)
RexxMutableBuffer * upper(RexxInteger *_start, RexxInteger *_length)
RexxInteger * countStrRexx(RexxString *needle)
RexxInteger * matchChar(RexxInteger *position_, RexxString *matchSet)
RexxString * makeString()
RexxObject * lengthRexx()
RexxInteger * caselessCountStrRexx(RexxString *needle)
char * setCapacity(sizeB_t newLength)
void copyData(sizeB_t offset, const char *string, sizeB_t l)
RexxInteger * wordLength(RexxInteger *)
RexxInteger * wordIndex(RexxInteger *)
RexxObject * setBufferSize(RexxInteger *)
static RexxClass * classInstance
RexxInteger * lastPos(RexxString *needle, RexxInteger *_start, RexxInteger *_range)
RexxMutableBuffer * overlay(RexxObject *, RexxObject *, RexxObject *, RexxObject *)
RexxInteger * caselessMatchChar(RexxInteger *position_, RexxString *matchSet)
RexxMutableBuffer * replaceAt(RexxObject *str, RexxObject *pos, RexxObject *len, RexxObject *pad)
RexxString * subWord(RexxInteger *, RexxInteger *)
RexxString * primitiveMakeString()
RexxObject * setBufferLength(sizeB_t)
RexxInteger * match(RexxInteger *start_, RexxString *other, RexxInteger *offset_, RexxInteger *len_)
RexxInteger * caselessPos(RexxString *needle, RexxInteger *_start, RexxInteger *_range)
RexxString * word(RexxInteger *)
RexxString * substr(RexxInteger *startPosition, RexxInteger *len, RexxString *pad)
RexxMutableBuffer * insert(RexxObject *, RexxObject *, RexxObject *, RexxObject *)
void sendMessage(RexxString *, RexxArray *, RexxDirectory *, ProtectedObject &)
RexxObject * makeArrayRexx()
const char * getStringData()
char getCharB(sizeB_t p)
sizeB_t getBLength()
static RexxInteger * posRexx(const char *stringData, sizeB_t length, RexxString *needle, RexxInteger *pstart, RexxInteger *range)
Definition: StringUtil.cpp:130
static RexxInteger * wordIndex(const char *data, sizeB_t length, RexxInteger *position)
static size_t memPos(const char *string, sizeB_t length, char target)
static sizeB_t caselessLastPos(const char *stringData, sizeB_t haystackLen, RexxString *needle, sizeB_t _start, sizeB_t range)
Definition: StringUtil.cpp:360
static RexxString * substr(const char *, sizeB_t, RexxInteger *, RexxInteger *, RexxString *)
Definition: StringUtil.cpp:66
static size_t caselessCountStr(const char *hayStack, sizeB_t hayStackLength, RexxString *needle)
static RexxString * word(const char *data, sizeB_t length, RexxInteger *position)
static void skipBlanks(const char **String, sizeB_t *StringLength)
static RexxInteger * wordPos(const char *data, sizeB_t length, RexxString *phrase, RexxInteger *pstart)
static RexxInteger * verify(const char *data, sizeB_t stringLen, RexxString *ref, RexxString *option, RexxInteger *_start, RexxInteger *range)
static RexxInteger * wordLength(const char *data, sizeB_t length, RexxInteger *position)
static RexxInteger * caselessWordPos(const char *data, sizeB_t length, RexxString *phrase, RexxInteger *pstart)
static RexxArray * subWords(const char *data, sizeB_t length, RexxInteger *position, RexxInteger *plength)
static size_t countStr(const char *hayStack, sizeB_t hayStackLength, RexxString *needle)
static sizeB_t pos(const char *stringData, sizeB_t haystack_length, RexxString *needle, sizeB_t _start, sizeB_t _range)
Definition: StringUtil.cpp:155
static RexxString * subWord(const char *data, sizeB_t length, RexxInteger *position, RexxInteger *plength)
static size_t wordCount(const char *String, sizeB_t StringLength)
static sizeB_t nextWord(const char **String, sizeB_t *StringLength, const char **NextString)
static RexxInteger * lastPosRexx(const char *stringData, sizeB_t haystackLen, RexxString *needle, RexxInteger *_start, RexxInteger *_range)
Definition: StringUtil.cpp:255
static sizeB_t caselessPos(const char *stringData, sizeB_t haystack_length, RexxString *needle, sizeB_t _start, sizeB_t _range)
Definition: StringUtil.cpp:205
static int caselessCompare(const char *, const char *, sizeB_t)
Definition: StringUtil.cpp:580
static RexxArray * makearray(const char *start, sizeB_t length, RexxString *separator)
Definition: StringUtil.cpp:493
static RexxString * subchar(const char *stringData, sizeB_t stringLength, RexxInteger *positionArg)
Definition: StringUtil.cpp:442
stringsize_t stringsizeB_t
Definition: rexx.h:247
ssize_t codepoint_t
Definition: rexx.h:232
#define sizeB_v(X)
Definition: rexx.h:250
#define size_v(X)
Definition: rexx.h:237
stringsizeB_t sizeB_t
Definition: rexx.h:248