StringClassConversion.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 StringClassConversion.cpp */
40 /* */
41 /* REXX string conversion methods */
42 /* */
43 /******************************************************************************/
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <math.h>
48 #include "RexxCore.h"
49 #include "StringClass.hpp"
50 #include "BufferClass.hpp"
51 
52 #include "NumberStringMath.hpp"
53 #include "ActivityManager.hpp"
54 #include "StringUtil.hpp"
55 
56 
57 /**
58  * Convert the character string into the same string with the
59  * characters converted into a Base64 encoding.
60  *
61  * @return The string reformatted into a Base64 encoding.
62  */
63 // in behaviour
65 {
66  size_t inc[3];
67  int i;
68  static const char cb64[] =
69  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
70 
71 
72  size_t inputLength = this->getLength(); /* get length of string */
73  if (inputLength == 0) /* null string? */
74  {
75  return OREF_NULLSTRING;
76  }
77  /* figure out the output string length */
78  size_t outputLength = (inputLength / 3) * 4;
79  if (inputLength % 3 > 0)
80  {
81  outputLength += 4;
82  }
83  /* allocate output string */
84  RexxString *retval = raw_string(outputLength);
85  const char *source = this->getStringData(); /* point to converted string */
86  /* point to output area */
87  char *destination = retval->getWritableData();
88  while (inputLength > 0)
89  { /* while more string */
90  int buflen = 0;
91  for (i = 0; i < 3; i++)
92  { /* get the next 3 characters */
93  if (inputLength != 0)
94  { /* from the input string */
95  inc[i] = *source & 0xff;
96  inputLength--;
97  source++;
98  buflen++;
99  }
100  else
101  {
102  inc[i] = '\0';
103  }
104  }
105  if (buflen) {
106  /* now perform the base64 conversion to the next 4 output string chars */
107  *destination = cb64[ inc[0] >> 2 ];
108  destination++;
109  *destination = cb64[ ((inc[0] & 0x03) << 4) | ((inc[1] & 0xf0) >> 4) ];
110  destination++;
111  *destination = (char) (buflen > 1 ? cb64[ ((inc[1] & 0x0f) << 2) | ((inc[2] & 0xc0) >> 6) ] : '=');
112  destination++;
113  *destination = (char) (buflen > 2 ? cb64[ inc[2] & 0x3f ] : '=');
114  destination++;
115  }
116  } /* done building the string */
117  return retval; /* return converted string */
118 }
119 
120 
121 /**
122  * Reverse the effect of an encodebase64 operation, converting
123  * a string in Base64 format into a "normal" character string.
124  *
125  * @return The converted character string.
126  */
127 // in behaviour
129 {
130  unsigned int i, j;
131  static const char cb64[] =
132  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
133 
134 
135  size_t inputLength = this->getLength(); /* get length of string */
136  if (inputLength == 0) /* null string? */
137  {
138  return OREF_NULLSTRING; // this encodes as a null string
139  }
140  if (inputLength % 4 > 0) {
141  /* the input string is an invalid length */
143  }
144  const char *source = this->getStringData();
145  /* figure out the output string length */
146  size_t outputLength = (inputLength / 4) * 3;
147  if (*(source + inputLength - 1) == '=')
148  {
149  outputLength--;
150  }
151  if (*(source + inputLength - 2) == '=')
152  {
153  outputLength--;
154  }
155  /* allocate output string */
156  RexxString *retval = raw_string(outputLength);
157  /* point to output area */
158  char *destination = retval->getWritableData();
159  while (inputLength != 0)
160  { /* while more string */
161  for (i = 0; i < 4; i++)
162  {
163  for (j = 0; j < 64; j++)
164  {
165  if (*(cb64 + j) == *(source + i))
166  {
167  break;
168  }
169  }
170  // if we don't find the digit, this might be an invalid string.
171  if (j == 64)
172  {
173  if (*(source + i) == '=' && inputLength <= 4) {
174  /* this means we are done building the output string */
175  break;
176  }
177  else {
178  /* we found an invalid char in the middle of the input string */
180  }
181  }
182  /* j is now the 6-bit value we need for building our output string */
183  switch (i)
184  {
185  case 0:
186  *destination = (char)(j << 2);
187  break;
188  case 1:
189  *destination |= (char)(j >> 4);
190  destination++;
191  *destination = (char)(j << 4);
192  break;
193  case 2:
194  *destination |= (char)(j >> 2);
195  destination++;
196  *destination = (char)(j << 6);
197  break;
198  case 3:
199  *destination |= (char)j;
200  destination++;
201  break;
202  default: /* not really necessary but here anyway */
203  break;
204  }
205  }
206  source += 4;
207  inputLength -= 4;
208  } /* done building the string */
209  return retval; /* return converted string */
210 }
211 
212 // in behaviour
214 /******************************************************************************/
215 /* Function: Process the string C2X method/function */
216 /******************************************************************************/
217 {
218  size_t InputLength; /* length of converted string */
219  RexxString *Retval; /* return value */
220  const char *Source; /* input string pointer */
221  char * Destination; /* output string pointer */
222  char ch; /* current character */
223 
224  InputLength = this->getLength(); /* get length of string */
225  if (InputLength == 0) /* null string? */
226  {
227  Retval = OREF_NULLSTRING; /* converts to a null string */
228  }
229  else
230  { /* real data to convert */
231  /* allocate output string */
232  Retval = raw_string(InputLength * 2);
233  Source = this->getStringData(); /* point to converted string */
234  /* point to output area */
235  Destination = Retval->getWritableData();
236  while (InputLength-- != 0)
237  { /* while more string */
238  ch = *Source++; /* get next character */
239  /***********************************************************/
240  /* get upper nibble after shifting out lower nibble and do */
241  /* logical ANDING with F to convert to integer then convert*/
242  /* to hex value and put it in destination */
243  /***********************************************************/
244  *Destination++ = IntToHexDigit((ch>>4) & 0xF);
245  /***********************************************************/
246  /* logical AND with F to convert lower nibble to integer */
247  /* then convert to hex value and put it in destination */
248  /***********************************************************/
249  *Destination++ = IntToHexDigit(ch & 0xF);
250  }
251  }
252  return Retval; /* return converted string */
253 }
254 
255 // in behaviour
257 /******************************************************************************/
258 /* Function: Process the string D2C method/function */
259 /******************************************************************************/
260 {
261  /* convert to a numberstring */
262  RexxNumberString *numberstring = this->numberString();
263  if (numberstring == OREF_NULL) /* not a valid number? */
264  {
265  /* report this */
267  }
268  /* format as a string value */
269  return numberstring->d2xD2c(_length, true);
270 }
271 
272 // in behaviour
274 /******************************************************************************/
275 /* Function: Process the string D2X method/function */
276 /******************************************************************************/
277 {
278  /* convert to a numberstring */
279  RexxNumberString *numberstring = this->numberString();
280  if (numberstring == OREF_NULL) /* not a valid number? */
281  {
282  /* report this */
284  }
285  /* format as a string value */
286  return numberstring->d2xD2c(_length, false);
287 }
288 
289 // in behaviour
291 /******************************************************************************/
292 /* Function: Process the string X2C method/function */
293 /******************************************************************************/
294 {
295  size_t InputLength = this->getLength(); /* get length of string */
296  if (InputLength== 0) /* null string? */
297  {
298  return OREF_NULLSTRING; /* converts to a null string */
299  }
300  else /* real data to convert */
301  {
302  /* try to pack the data */
303  return StringUtil::packHex(this->getStringData(), InputLength);
304  }
305 }
306 
307 // in behaviour
309 /******************************************************************************/
310 /* Function: Process the string X2D method/function */
311 /******************************************************************************/
312 {
313  /* forward to the common routine */
314  return this->x2dC2d(_length, false);
315 }
316 
317 
319  bool type )
320 /******************************************************************************/
321 /* Function: Common X2D/X2C processing routine */
322 /******************************************************************************/
323 {
324  size_t ResultSize; /* size of result string */
325  size_t TempSize; /* temporary size value */
326  int ch; /* addition character */
327  size_t StringLength; /* input string length */
328  char *Scan; /* scan pointer */
329  char *HighDigit; /* high digit position */
330  char * Accumulator; /* accumulator pointer */
331  bool Negative; /* have a negative number */
332  RexxString *String; /* converted string */
333  char *StringPtr; /* string value pointer */
334  size_t BytePosition; /* position of high byte */
335  size_t NibblePosition; /* position of high nibble */
336  size_t DecLength; /* length of accumulator */
337  size_t TempLength; /* length of accumulator */
338  RexxString *Retval; /* function return value */
339  RexxBuffer *Buffer; /* first math buffer */
340  size_t CurrentDigits; /* current digits setting */
341 
342  CurrentDigits = number_digits(); /* get the current digits setting */
343  StringLength = this->getLength(); /* get Argument string length */
344  /* get the target length */
345  ResultSize = optionalLengthArgument(_length, -1, ARG_ONE);
346  if (ResultSize == 0) /* zero requested */
347  {
348  return(RexxString *)IntegerZero; /* always returns zero */
349  }
350 
351  String = this; /* use this string directly */
352  StringPtr = this->getWritableData(); /* get a string pointer */
353  NibblePosition = 0; /* assume an even nibble number */
354 
355  if (type == true)
356  { /* dealing with character? */
357  if (_length == OREF_NULL)
358  { /* no size specified? */
359  Negative = false; /* can't be negative */
360  ResultSize = StringLength; /* use entire string */
361  }
362  else
363  { /* have to check for negative */
364  if (ResultSize > StringLength) /* longer than string? */
365  {
366  Negative = false; /* can't be negative */
367  }
368  else
369  { /* have to check sign bit */
370  /* step to byte position */
371  StringPtr += StringLength - ResultSize;
372  StringLength = ResultSize; /* adjust the size down */
373 
374  if (*StringPtr & 0x80)
375  { /* first bit on? */
376  Negative = true; /* this is a negative number */
377  /* copy the string */
378  String = (RexxString *)this->copy();
379  /* point to the string */
380  StringPtr = String->getWritableData() + this->getLength() - ResultSize;
381  }
382  else /* still a positive number */
383  {
384  Negative = false; /* remember for later */
385  }
386  }
387  }
388  }
389  else
390  { /* x2d function */
391  /* pack the string */
392  String = (RexxString *)StringUtil::packHex(StringPtr, StringLength);
393  /* get the packed length */
394  StringLength = String->getLength();
395  /* point to the packed data */
396  StringPtr = String->getWritableData();
397  if (_length == OREF_NULL)
398  { /* no size specified? */
399  Negative = false; /* can't be negative */
400  ResultSize = StringLength; /* use entire string */
401  }
402  else
403  { /* have to check for negative */
404 
405  BytePosition = ResultSize / 2; /* Get position of sign bit */
406  /* get nibble position */
407  NibblePosition = ResultSize % 2;
408  /* Get result size */
409  ResultSize = (BytePosition + NibblePosition);
410  if (ResultSize > StringLength)
411  { /* longer than string? */
412  Negative = false; /* can't be negative */
413  NibblePosition = 0; /* leave the high nibble alone */
414  }
415  else
416  { /* have to check sign bit */
417  /* step to byte position */
418  StringPtr += StringLength - ResultSize;
419  StringLength = ResultSize; /* adjust the size down */
420 
421  if ((NibblePosition != 0 && /* odd number of nibbles */
422  /* and low nibble negative? */
423  *StringPtr & 0x08) ||
424  (NibblePosition == 0 && /* or even number of nibbles */
425  *StringPtr & 0x80)) /* and high nibble negative? */
426  {
427  Negative = true; /* this is a negative number */
428  }
429  else /* still a positive number */
430  {
431  Negative = false; /* remember for later */
432  }
433  }
434  }
435  }
436 
437  if (Negative)
438  { /* need to negate string? */
439  Scan = StringPtr; /* copy the pointer */
440  TempSize = StringLength; /* copy the size */
441 
442  while (TempSize-- != 0)
443  { /* reverse each byte */
444  /* exclusive or with foxes */
445  *Scan = *Scan ^ 0xff;
446  Scan++; /* step the pointer */
447  }
448  /* point to the first byte */
449  Scan = StringPtr + StringLength - 1;
450  TempSize = StringLength; /* copy the size */
451  while (TempSize-- != 0)
452  { /* now add one to the number */
453  ch = (*Scan & 0xff); /* get the character */
454  ch++; /* increment */
455  if (ch <= 0xff)
456  { /* no carry over? */
457  *Scan = ch; /* set value back */
458  break; /* we're finished */
459  }
460  else
461  { /* carried out */
462  *Scan = 0; /* this is zero now */
463  Scan--; /* step back one pointer */
464  }
465  }
466  }
467  if (NibblePosition != 0) /* Odd number of nibbles? */
468  {
469  *StringPtr &= 0x0f; /* zero out the highest nibble */
470  }
471 
472  Scan = StringPtr; /* point to the string */
473  /* allocate a temp buffer */
474  Buffer = (RexxBuffer *)new_buffer(CurrentDigits + OVERFLOWSPACE + 1);
475  /* set accumulator pointer */
476  Accumulator = Buffer->getData() + CurrentDigits + OVERFLOWSPACE;
477  /* clear the buffer */
478  memset(Buffer->getData(), '\0', CurrentDigits + OVERFLOWSPACE + 1);
479  HighDigit = Accumulator - 1; /* set initial high point */
480 
481  while (StringLength-- != 0)
482  { /* while more digits */
483  ch = *Scan++; /* get the character */
484  /* add high order nibble */
485  HighDigit = RexxNumberString::addToBaseTen((ch & 0xf0) >> 4, Accumulator, HighDigit);
486  /* multiply by 16 */
487  HighDigit = RexxNumberString::multiplyBaseTen(Accumulator, HighDigit);
488  /* get accumulator length */
489  DecLength = (Accumulator - HighDigit);
490  if (DecLength > CurrentDigits)
491  { /* grown too long? */
492  if (type == true) /* c2d version? */
493  {
495  }
496  else /* this is the x2d function */
497  {
499  }
500  }
501  /* add high order nibble */
502  HighDigit = RexxNumberString::addToBaseTen(ch & 0x0f, Accumulator, HighDigit);
503  if (StringLength != 0) /* not the last one? */
504  {
505  /* multiply by 16 */
506  HighDigit = RexxNumberString::multiplyBaseTen(Accumulator, HighDigit);
507  }
508  /* get accumulator length */
509  DecLength = (Accumulator - HighDigit);
510  if (DecLength > CurrentDigits)
511  { /* grown too long? */
512  if (type == true) /* c2d version? */
513  {
515  }
516  else /* this is the x2d function */
517  {
519  }
520  }
521  }
522  /* get accumulator length */
523  DecLength = (Accumulator - HighDigit);
524  TempLength = DecLength; /* copy the length */
525  Scan = HighDigit + 1; /* point to the first digit */
526  while (TempLength--)
527  { /* make into real digits again */
528  /* add zero to each digit */
529  *Scan = *Scan + '0';
530  Scan++; /* step the pointer */
531  }
532 
533  ResultSize = DecLength; /* get the result size */
534  if (Negative) /* negative number? */
535  {
536  ResultSize++; /* add in space for the sign */
537  }
538  Retval = raw_string(ResultSize); /* allocate output buffer */
539  Scan = Retval->getWritableData(); /* point to output location */
540  if (Negative) /* need a sign? */
541  {
542  *Scan++ = '-'; /* add to the front */
543  }
544  /* copy in the number */
545  memcpy(Scan, Accumulator - DecLength + 1, DecLength);
546  return Retval; /* return converted string */
547 }
548 
549 // in behaviour
551 /******************************************************************************/
552 /* Function: Common B2X processing routine */
553 /******************************************************************************/
554 {
555  RexxString *Retval; /* function result */
556  size_t Bits; /* number of bits in string */
557  const char *Source; /* current source pointer */
558  char *Destination; /* destination pointer */
559  size_t Excess; /* section boundary */
560  char Nibble[4]; /* current nibble string */
561  size_t Jump; /* string movement offset */
562  size_t Length; /* total string length */
563 
564  if (this->getLength() == 0) /* null input, i.e. zerolength */
565  {
566  Retval = OREF_NULLSTRING; /* return null */
567  }
568  else
569  { /* need to do conversion */
570  /* validate the string */
571  Bits = StringUtil::validateSet(this->getStringData(), this->getLength(), "01", 4, false);
572  /* allocate space for result */
573  Retval = raw_string((Bits + 3) / 4);
574  /* point to the data */
575  Destination = Retval->getWritableData();
576  Source = this->getStringData(); /* point to the source */
577  Length = this->getLength(); /* get the string length */
578 
579  while (Bits > 0)
580  { /* process the string */
581  Excess = Bits % 4; /* calculate section size */
582  if (Excess == 0) /* zero is a multiple of 4 */
583  {
584  Excess = 4; /* so use 4 */
585  }
586  else
587  {
588  memset(Nibble, '0', 4); /* pad the nibble with zeroes */
589  }
590  StringUtil::chGetSm(&Nibble[0] + (4 - Excess), Source, Length, Excess, "01", &Jump);
591  /* pack into destination */
592  *Destination++ = StringUtil::packNibble(Nibble);
593  Source += Jump; /* advance source pointer */
594  Length -= Jump; /* reduce remaining length */
595  Bits -= Excess; /* reduce remaining amount */
596  }
597  }
598  return Retval; /* return packed string */
599 }
600 
601 // in behaviour
603 /******************************************************************************/
604 /* Function: Common C2D processing routine */
605 /******************************************************************************/
606 {
607  /* forward to the common routine */
608  return this->x2dC2d(_length, true);
609 }
610 
611 // in behaviour
613 /******************************************************************************/
614 /* Function: Common X2B processing routine */
615 /******************************************************************************/
616 {
617  RexxString *Retval; /* function result */
618  size_t Nibbles; /* nibbles in hex string */
619  const char *Source; /* current source pointer */
620  char *Destination; /* destination pointer */
621  char Nibble[4]; /* current nibble string */
622  char ch; /* current string character */
623  int Val; /* converted nible */
624 
625  if (this->getLength() == 0) /* null input, i.e. zerolength */
626  {
627  /* string */
628  Retval = OREF_NULLSTRING; /* return null */
629  }
630  else
631  { /* have real data to pack */
632  Nibbles = StringUtil::validateSet(this->getStringData(), this->getLength(), "0123456789ABCDEFabcdef", 2, true);
633  Retval = raw_string(Nibbles * 4); /* allocate result string */
634  /* point to the data */
635  Destination = Retval->getWritableData();
636  Source = this->getStringData(); /* point to the source */
637 
638  while (Nibbles > 0)
639  { /* while still string to pack */
640  ch = *Source++; /* get current char and bump */
641  /* pointer */
642  if (ch != ch_SPACE && ch != ch_TAB)
643  { /* if not a filler space */
644  Val = StringUtil::hexDigitToInt(ch); /* convert hex to int first */
645  StringUtil::unpackNibble(Val, Nibble); /* then convert to binary */
646  /* digits */
647  /* copy to the destination */
648  memcpy(Destination, Nibble, 4);
649  Destination += 4; /* bump destination pointer */
650  Nibbles--; /* Reduce nibbles count */
651  }
652  }
653  }
654  return Retval; /* return the expanded string */
655 }
void reportException(wholenumber_t error)
RexxBuffer * new_buffer(size_t s)
#define ch_TAB
#define OVERFLOWSPACE
size_t number_digits()
Definition: Numerics.hpp:147
#define OREF_NULL
Definition: RexxCore.h:61
size_t optionalLengthArgument(RexxObject *o, size_t d, size_t p)
Definition: RexxCore.h:355
const int ARG_ONE
Definition: RexxCore.h:83
#define IntegerZero
Definition: RexxCore.h:199
#define Error_Incorrect_method_d2x
#define Error_Incorrect_method_d2c
#define Error_Incorrect_method_x2dbig
#define Error_Incorrect_method_invbase64
#define Error_Incorrect_method_c2dbig
RexxString * raw_string(stringsize_t l)
char IntToHexDigit(int n)
#define ch_SPACE
Definition: StringClass.hpp:92
size_t RexxEntry StringLength(RexxThreadContext *c, RexxStringObject s)
virtual char * getData()
static char * addToBaseTen(int, char *, char *)
RexxString * d2xD2c(RexxObject *, bool)
static char * multiplyBaseTen(char *, char *)
RexxObject * copy()
RexxString * c2x()
size_t getLength()
RexxString * b2x()
const char * getStringData()
RexxString * encodeBase64()
RexxString * d2x(RexxInteger *)
RexxNumberString * numberString()
RexxString * d2c(RexxInteger *)
RexxString * x2dC2d(RexxInteger *, bool)
char * getWritableData()
RexxString * x2d(RexxInteger *)
RexxString * c2d(RexxInteger *)
RexxString * x2b()
RexxString * x2c()
RexxString * decodeBase64()
static void unpackNibble(int Val, char *p)
Definition: StringUtil.cpp:949
static size_t validateSet(const char *String, size_t Length, const char *Set, int Modulus, bool Hex)
Definition: StringUtil.cpp:726
static RexxString * packHex(const char *String, size_t StringLength)
Definition: StringUtil.cpp:875
static char packNibble(const char *String)
Definition: StringUtil.cpp:668
static int hexDigitToInt(char ch)
Definition: StringUtil.cpp:618
static size_t chGetSm(char *Destination, const char *Source, size_t Length, size_t Count, const char *Set, size_t *ScannedSize)
Definition: StringUtil.cpp:835
int type
Definition: cmdparse.cpp:1888