String: share a common empty string value
[scilab.git] / scilab / modules / ast / src / cpp / types / string.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2008-2008 - DIGITEO - Antoine ELIAS
4 *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13 *
14 */
15
16 #include <sstream>
17 #include <iomanip>
18 #include "core_math.h"
19 #include "string.hxx"
20 #include "stringexp.hxx"
21 #include "tostring_common.hxx"
22 #include "configvariable.hxx"
23 #include "type_traits.hxx"
24
25 extern "C"
26 {
27 #include "charEncoding.h"
28 #include "os_string.h"
29 #include "sci_malloc.h"
30 }
31
32 #define SIZE_BETWEEN_TWO_STRING_VALUES  2
33 #define SPACE_BETWEEN_TWO_STRING_VALUES L"  "
34
35 namespace types
36 {
37 String::~String()
38 {
39     if (isDeletable() == true)
40     {
41         deleteAll();
42     }
43 #ifndef NDEBUG
44     Inspector::removeItem(this);
45 #endif
46 }
47
48 String::String(int _iDims, const int* _piDims)
49 {
50     wchar_t** pwsData = NULL;
51     create(_piDims, _iDims, &pwsData, NULL);
52 #ifndef NDEBUG
53     Inspector::addItem(this);
54 #endif
55 }
56
57 String::String(const wchar_t* _pwstData)
58 {
59     wchar_t** pwsData = NULL;
60     int piDims[] = {1, 1};
61     create(piDims, 2, &pwsData, NULL);
62     set(0, 0, _pwstData);
63 #ifndef NDEBUG
64     Inspector::addItem(this);
65 #endif
66 }
67
68 String::String(const char *_pstData)
69 {
70     wchar_t** pwsData = NULL;
71     int piDims[] = {1, 1};
72     create(piDims, 2, &pwsData, NULL);
73     wchar_t* data = to_wide_string(const_cast<char*>(_pstData));
74     set(0, 0, data);
75     FREE(data);
76 #ifndef NDEBUG
77     Inspector::addItem(this);
78 #endif
79 }
80
81 String::String(int _iRows, int _iCols)
82 {
83     wchar_t** pwsData = NULL;
84     int piDims[] = {_iRows, _iCols};
85     create(piDims, 2, &pwsData, NULL);
86 #ifndef NDEBUG
87     Inspector::addItem(this);
88 #endif
89 }
90
91 String::String(int _iRows, int _iCols, wchar_t const* const* _pstData)
92 {
93     wchar_t** pwsData = NULL;
94     int piDims[] = {_iRows, _iCols};
95     create(piDims, 2, &pwsData, NULL);
96     for (int i = 0 ; i < m_iSize ; i++)
97     {
98         set(i, _pstData[i]);
99     }
100 #ifndef NDEBUG
101     Inspector::addItem(this);
102 #endif
103 }
104
105 String* String::clone()
106 {
107     String *pstClone = new String(getDims(), getDimsArray());
108     pstClone->set(m_pRealData);
109     return pstClone;
110 }
111
112 void String::whoAmI()
113 {
114     std::cout << "types::String";
115 }
116
117 void String::deleteString(int _iPos)
118 {
119     if (m_pRealData != NULL)
120     {
121         if (m_pRealData[_iPos] != NULL && m_pRealData[_iPos] != String::nullValue())
122         {
123             FREE(m_pRealData[_iPos]);
124             m_pRealData[_iPos] = NULL;
125         }
126     }
127 }
128
129 void String::deleteAll()
130 {
131     for (int i = 0 ; i < m_iSizeMax ; i++)
132     {
133         deleteString(i);
134     }
135     delete[] m_pRealData;
136     m_pRealData = NULL;
137 }
138
139 void String::deleteImg()
140 {
141     return;
142 }
143
144 bool String::subMatrixToString(std::wostringstream& ostr, int* _piDims, int /*_iDims*/)
145 {
146     int iPrecision = ConfigVariable::getFormatSize();
147     int iLineLen = ConfigVariable::getConsoleWidth();
148     int iMaxLines = ConfigVariable::getConsoleLines();
149     int iCurrentLine = 0;
150
151     // less the two "!" (or two " " in scalar case)
152     // for iLineLen = 50 we will write "!48char!"
153     int iStrMaxSize = iLineLen - 2;
154
155     if (isScalar())
156     {
157         int iMaxLen = 0;
158         _piDims[0]  = 0;
159         _piDims[1]  = 0;
160
161         int iPos = getIndex(_piDims);
162         wchar_t* wcsStr = get(iPos);
163         int iCurLen = static_cast<int>(wcslen(wcsStr));
164         iMaxLen = std::max(iMaxLen, iCurLen);
165         iMaxLen = std::min(iMaxLen, iStrMaxSize);
166
167         if (iCurLen > iMaxLen)
168         {
169             int iStrPos = 0;
170             while (iCurLen > iStrMaxSize)
171             {
172                 ostr << L" ";
173                 ostr.write(wcsStr + iStrPos, iStrMaxSize);
174                 ostr << L" " << std::endl;
175                 iCurLen -= iStrMaxSize;
176                 iStrPos += iStrMaxSize;
177             }
178
179             ostr << L" ";
180             configureStream(&ostr, iStrMaxSize, iPrecision, ' ');
181             ostr << std::left << wcsStr + iStrPos << std::resetiosflags(std::ios::adjustfield);
182         }
183         else
184         {
185             ostr << L" " << wcsStr << std::endl;
186         }
187     }
188     else if (getCols() == 1)
189     {
190         std::wstring spaces(L"");
191
192         // compte max string size
193         int iMaxLen = 0;
194         for (int i = 0 ; i < getRows() ; i++)
195         {
196             _piDims[1] = 0;
197             _piDims[0] = i;
198             int iPos = getIndex(_piDims);
199             iMaxLen = std::max(iMaxLen, static_cast<int>(wcslen(get(iPos))));
200             iMaxLen = std::min(iMaxLen, iStrMaxSize);
201         }
202
203         int iEmptyLineSize = iMaxLen;
204         if (iMaxLen != iStrMaxSize)
205         {
206             // count SPACE_BETWEEN_TWO_STRING_VALUES size in padding of empty line
207             // only if all lines have not a size greater than max size of a line.
208             iEmptyLineSize += SIZE_BETWEEN_TWO_STRING_VALUES;
209             spaces = SPACE_BETWEEN_TWO_STRING_VALUES;
210         }
211
212         for (int i = m_iRows1PrintState ; i < getRows() ; i++)
213         {
214             iCurrentLine += 2;
215             if ((iMaxLines == 0 && iCurrentLine >= MAX_LINES) || (iMaxLines != 0 && iCurrentLine >= iMaxLines))
216             {
217                 m_iRows1PrintState = i;
218                 return false;
219             }
220
221             _piDims[1] = 0;
222             _piDims[0] = i;
223             int iPos = getIndex(_piDims);
224             wchar_t* wcsStr = get(iPos);
225             int iCurLen = static_cast<int>(wcslen(wcsStr));
226
227             ostr << L"!";
228             if (iCurLen > iMaxLen)
229             {
230                 int iStrPos = 0;
231                 while (iCurLen > iStrMaxSize)
232                 {
233                     ostr.write(wcsStr + iStrPos, iStrMaxSize);
234                     ostr << L"!" << std::endl << L"!";
235                     iCurLen -= iStrMaxSize;
236                     iStrPos += iStrMaxSize;
237                 }
238
239                 configureStream(&ostr, iStrMaxSize, iPrecision, ' ');
240                 ostr << std::left << wcsStr + iStrPos;
241             }
242             else
243             {
244                 configureStream(&ostr, iMaxLen, iPrecision, ' ');
245                 ostr << std::left << wcsStr << spaces;
246             }
247
248             ostr << L"!" << std::endl;
249
250             if ((i + 1) < m_iSize)
251             {
252                 //for all but last one
253                 ostr << L"!";
254                 configureStream(&ostr, iEmptyLineSize, iPrecision, ' ');
255                 ostr << std::left << L" ";
256                 ostr << L"!" << std::endl;
257             }
258         }
259         ostr << std::resetiosflags(std::ios::adjustfield);
260     }
261     else if (getRows() == 1)
262     {
263         std::wostringstream ostemp;
264         int iLastVal = m_iCols1PrintState;
265
266         for (int i = m_iCols1PrintState ; i < getCols() ; i++)
267         {
268             _piDims[0] = 0;
269             _piDims[1] = i;
270             int iPos = getIndex(_piDims);
271
272             int iLen = 0;
273             int iCurLen = static_cast<int>(wcslen(get(iPos)));
274             iLen = iCurLen + SIZE_BETWEEN_TWO_STRING_VALUES + static_cast<int>(ostemp.str().size());
275             if (iLen > iLineLen && iLastVal != i)
276             {
277                 //Max length, new line
278                 iCurrentLine += 4; //"column x to Y" + empty line + value + empty line
279                 if ((iMaxLines == 0 && iCurrentLine >= MAX_LINES) || (iMaxLines != 0 && iCurrentLine >= iMaxLines))
280                 {
281                     m_iCols1PrintState = iLastVal;
282                     return false;
283                 }
284
285                 addColumnString(ostr, iLastVal + 1, i);
286                 ostr << L"!" << ostemp.str() << L"!" << std::endl;
287                 ostemp.str(L"");
288                 iLastVal = i;
289             }
290
291             // Manage case where string length is greater than max line size.
292             if (iStrMaxSize < iCurLen)
293             {
294                 wchar_t* wcsStr = get(iPos);
295                 int iStrPos = 0;
296                 while (iCurLen > iStrMaxSize) // -2 because of two "!"
297                 {
298                     ostemp.write(wcsStr + iStrPos, iStrMaxSize);
299                     ostemp << L"!" << std::endl << L"!";
300                     iCurLen -= iStrMaxSize;
301                     iStrPos += iStrMaxSize;
302                 }
303
304                 configureStream(&ostemp, iStrMaxSize, iPrecision, ' ');
305                 ostemp << std::left << wcsStr + iStrPos;
306             }
307             else
308             {
309                 configureStream(&ostemp, iCurLen + 2, iPrecision, ' ');
310                 ostemp << std::left << get(iPos);
311             }
312         }
313
314         if (iLastVal != 0)
315         {
316             addColumnString(ostr, iLastVal + 1, getCols());
317         }
318
319         ostr << L"!" << ostemp.str() << L"!" << std::endl << std::resetiosflags(std::ios::adjustfield);
320     }
321     else //Matrix
322     {
323         std::wostringstream ostemp;
324         int iLen = 0;
325         int iLastCol = m_iCols1PrintState;
326
327         //Array with the max printed size of each col
328         int *piSize = new int[getCols()];
329         memset(piSize, 0x00, getCols() * sizeof(int));
330
331         for (int iCols1 = m_iCols1PrintState ; iCols1 < getCols() ; iCols1++)
332         {
333             std::wstring spaces(L"");
334             for (int iRows1 = 0 ; iRows1 < getRows() ; iRows1++)
335             {
336                 _piDims[1] = iCols1;
337                 _piDims[0] = iRows1;
338                 int iPos = getIndex(_piDims);
339                 piSize[iCols1] = std::max(piSize[iCols1], static_cast<int>(wcslen(get(iPos))));
340                 piSize[iCols1] = std::min(piSize[iCols1], iStrMaxSize);
341             }
342
343             int iEmptyLineSize = piSize[iLastCol];
344             if (piSize[iLastCol] != iStrMaxSize)
345             {
346                 // count SPACE_BETWEEN_TWO_STRING_VALUES size in padding of empty line
347                 // only if all lines have not a size greater than max size of a line.
348                 iEmptyLineSize += SIZE_BETWEEN_TWO_STRING_VALUES;
349                 spaces = SPACE_BETWEEN_TWO_STRING_VALUES;
350             }
351
352             if (iLen + piSize[iCols1] > iLineLen && iLastCol != iCols1)
353             {
354                 //find the limit, print this part
355                 for (int iRows2 = m_iRows2PrintState ; iRows2 < getRows() ; iRows2++)
356                 {
357                     iCurrentLine += 2;
358                     if ((iMaxLines == 0 && iCurrentLine >= MAX_LINES) ||
359                             ( (iMaxLines != 0 && iCurrentLine + 3 >= iMaxLines && iRows2 == m_iRows2PrintState) ||
360                               (iMaxLines != 0 && iCurrentLine + 1 >= iMaxLines && iRows2 != m_iRows2PrintState)))
361                     {
362                         if (m_iRows2PrintState == 0 && iRows2 != 0)
363                         {
364                             //add header
365                             addColumnString(ostr, iLastCol + 1, iCols1);
366                         }
367                         ostr << ostemp.str();
368                         m_iRows2PrintState = iRows2;
369                         m_iCols1PrintState = iLastCol;
370                         delete[] piSize;
371                         return false;
372                     }
373
374                     ostemp << L"!";
375                     for (int iCols2 = iLastCol; iCols2 < iCols1; iCols2++)
376                     {
377                         _piDims[0] = iRows2;
378                         _piDims[1] = iCols2;
379                         int iPos = getIndex(_piDims);
380                         wchar_t* wcsStr = get(iPos);
381                         int iLenStr = static_cast<int>(wcslen(wcsStr));
382
383                         // Manage case where string length is greater than max line size.
384                         if (iLenStr > iStrMaxSize)
385                         {
386                             int iStrPos = 0;
387                             while (iLenStr > iStrMaxSize)
388                             {
389                                 ostemp.write(wcsStr + iStrPos, iStrMaxSize);
390                                 ostemp << L"!" << std::endl << L"!";
391                                 iLenStr -= iStrMaxSize;
392                                 iStrPos += iStrMaxSize;
393                             }
394
395                             configureStream(&ostemp, iStrMaxSize, iPrecision, ' ');
396                             ostemp << std::left << wcsStr + iStrPos;
397                         }
398                         else
399                         {
400                             configureStream(&ostemp, piSize[iCols2], iPrecision, ' ');
401                             ostemp << std::left << get(iPos) << spaces;
402                         }
403                     }
404                     ostemp << L"!" << std::endl;
405
406                     if ((iRows2 + 1) != m_iRows)
407                     {
408                         ostemp << L"!";
409                         // -2 because of two "!"
410                         configureStream(&ostemp, iEmptyLineSize, iPrecision, ' ');
411                         ostemp << std::left << L" ";
412                         ostemp << L"!" << std::endl;
413                     }
414                 }
415
416                 iLen = 0;
417                 iCurrentLine += 2;
418                 if (m_iRows2PrintState == 0)
419                 {
420                     iCurrentLine += 3;
421                     addColumnString(ostr, iLastCol + 1, iCols1);
422                 }
423
424                 ostr << ostemp.str();
425                 ostemp.str(L"");
426                 iLastCol = iCols1;
427                 m_iRows2PrintState = 0;
428                 m_iCols1PrintState = 0;
429             }
430             iLen += piSize[iCols1] + SIZE_BETWEEN_TWO_STRING_VALUES;
431         }
432
433         for (int iRows2 = m_iRows2PrintState ; iRows2 < getRows() ; iRows2++)
434         {
435             std::wstring spaces(L"");
436             iCurrentLine += 2;
437             if ((iMaxLines == 0 && iCurrentLine >= MAX_LINES) || (iMaxLines != 0 && iCurrentLine >= iMaxLines))
438             {
439                 if (m_iRows2PrintState == 0 && iLastCol != 0)
440                 {
441                     //add header
442                     addColumnString(ostr, iLastCol + 1, getCols());
443                 }
444
445                 ostr << ostemp.str();
446                 m_iRows2PrintState = iRows2;
447                 m_iCols1PrintState = iLastCol;
448                 delete[] piSize;
449                 return false;
450             }
451
452             int iEmptyLineSize = piSize[iLastCol];
453             if (piSize[iLastCol] != iStrMaxSize)
454             {
455                 // count SPACE_BETWEEN_TWO_STRING_VALUES size in padding of empty line
456                 // only if all lines have not a size greater than max size of a line.
457                 iEmptyLineSize += SIZE_BETWEEN_TWO_STRING_VALUES;
458                 spaces = SPACE_BETWEEN_TWO_STRING_VALUES;
459             }
460
461
462             ostemp << L"!";
463             iLen = 0;
464             for (int iCols2 = iLastCol ; iCols2 < getCols() ; iCols2++)
465             {
466                 _piDims[0] = iRows2;
467                 _piDims[1] = iCols2;
468                 int iPos = getIndex(_piDims);
469                 wchar_t* wcsStr = get(iPos);
470                 int iLenStr = static_cast<int>(wcslen(wcsStr));
471
472                 // Manage case where string length is greater than max line size.
473                 if (iStrMaxSize < iLenStr)
474                 {
475                     int iStrPos = 0;
476                     while (iLenStr > iStrMaxSize)
477                     {
478                         ostemp.write(wcsStr + iStrPos, iStrMaxSize);
479                         ostemp << L"!" << std::endl << L"!";
480                         iLenStr -= iStrMaxSize;
481                         iStrPos += iStrMaxSize;
482                     }
483
484                     configureStream(&ostemp, iStrMaxSize, iPrecision, ' ');
485                     ostemp << wcsStr + iStrPos << std::left;
486                     iLen = iStrMaxSize;
487                 }
488                 else
489                 {
490                     configureStream(&ostemp, piSize[iCols2], iPrecision, ' ');
491                     ostemp << std::left << get(iPos) << spaces;
492                     iLen += piSize[iCols2] + static_cast<int>(spaces.size());
493                 }
494             }
495             ostemp << L"!" << std::endl;
496
497             if ((iRows2 + 1) != m_iRows)
498             {
499                 ostemp << L"!";
500                 configureStream(&ostemp, iLen, iPrecision, ' ');
501                 ostemp << std::left << L" ";
502                 ostemp << L"!" << std::endl;
503             }
504         }
505
506         if (m_iRows2PrintState == 0 && iLastCol != 0)
507         {
508             addColumnString(ostr, iLastCol + 1, getCols());
509         }
510         ostr << ostemp.str() << std::resetiosflags(std::ios::adjustfield);
511         delete[] piSize;
512     }
513
514     return true;
515 }
516
517 bool String::operator==(const InternalType& it)
518 {
519     if (const_cast<InternalType&>(it).isString() == false)
520     {
521         return false;
522     }
523
524     String* pS = const_cast<InternalType&>(it).getAs<types::String>();
525
526     if (pS->getRows() != getRows() || pS->getCols() != getCols())
527     {
528         return false;
529     }
530
531     wchar_t **p1 = get();
532     wchar_t **p2 = pS->get();
533
534     for (int i = 0 ; i < getSize() ; i++)
535     {
536         if (wcscmp(p1[i], p2[i]) != 0)
537         {
538             return false;
539         }
540     }
541     return true;
542 }
543
544 bool String::operator!=(const InternalType& it)
545 {
546     return !(*this == it);
547 }
548
549 static std::wstring null = L"";
550 wchar_t* String::nullValue()
551 {
552     // The null value pointer is shared to speed up "" assignement
553     // Empty strings creation can then be done without memory allocation
554     return (wchar_t*) null.data();
555 }
556
557 String* String::createEmpty(int _iDims, int* _piDims, bool /*_bComplex*/)
558 {
559     return new String(_iDims, _piDims);
560 }
561
562 wchar_t* String::copyValue(wchar_t* _pwstData)
563 {
564     if (_pwstData == nullValue())
565     {
566         return nullValue();
567     }
568
569     try
570     {
571         return os_wcsdup(_pwstData);
572     }
573     catch (std::bad_alloc & /*e*/)
574     {
575         char message[bsiz];
576         os_sprintf(message, _("Can not allocate data.\n"));
577         throw ast::InternalError(message);
578     }
579
580     return NULL;
581 }
582
583 wchar_t* String::copyValue(const wchar_t* _pwstData)
584 {
585     if (_pwstData == nullValue())
586     {
587         return nullValue();
588     }
589
590     return os_wcsdup(_pwstData);
591 }
592
593 void String::deleteData(wchar_t* data)
594 {
595     if (data && data != nullValue())
596     {
597         // data are always allocated using C-like malloc API
598         FREE(data);
599     }
600 }
601
602 String* String::set(int _iPos, const wchar_t* _pwstData)
603 {
604     if (m_pRealData == NULL || _iPos >= m_iSize)
605     {
606         return NULL;
607     }
608
609     typedef String* (String::*set_t)(int, const wchar_t*);
610     String* pIT = checkRef(this, (set_t)&String::set, _iPos, _pwstData);
611     if (pIT != this)
612     {
613         return pIT;
614     }
615
616     deleteString(_iPos);
617     m_pRealData[_iPos] = copyValue(_pwstData);
618     return this;
619 }
620
621 String* String::set(int _iRows, int _iCols, const wchar_t* _pwstData)
622 {
623     int piIndexes[2] = {_iRows, _iCols};
624     return set(getIndex(piIndexes), _pwstData);
625 }
626
627 String* String::set(const wchar_t* const* _pwstData)
628 {
629     typedef String* (String::*set_t)(const wchar_t * const*);
630     String* pIT = checkRef(this, (set_t)&String::set, _pwstData);
631     if (pIT != this)
632     {
633         return pIT;
634     }
635
636     for (int i = 0; i < m_iSize; i++)
637     {
638         if (m_pRealData == NULL || i >= m_iSize)
639         {
640             return NULL;
641         }
642
643         deleteString(i);
644         m_pRealData[i] = copyValue(_pwstData[i]);
645     }
646     return this;
647 }
648
649 String* String::set(int _iPos, const char* _pcData)
650 {
651     wchar_t* w = to_wide_string(_pcData);
652     String* ret = set(_iPos, w);
653     FREE(w);
654     return ret;
655 }
656
657 String* String::set(int _iRows, int _iCols, const char* _pcData)
658 {
659     int piIndexes[2] = {_iRows, _iCols};
660     return set(getIndex(piIndexes), _pcData);
661 }
662
663 String* String::set(const char* const* _pstrData)
664 {
665     typedef String* (String::*set_t)(const char * const*);
666     String* pIT = checkRef(this, (set_t)&String::set, _pstrData);
667     if (pIT != this)
668     {
669         return pIT;
670     }
671
672     for (int i = 0; i < m_iSize; i++)
673     {
674         if (set(i, _pstrData[i]) == NULL)
675         {
676             return NULL;
677         }
678     }
679     return this;
680 }
681
682 wchar_t** String::allocData(int _iSize)
683 {
684     wchar_t** pStr = nullptr;
685     try
686     {
687         pStr = new wchar_t*[_iSize];
688         memset(pStr, 0x00, _iSize * sizeof(wchar_t*));
689     }
690     catch (std::bad_alloc & /*e*/)
691     {
692         char message[bsiz];
693         os_sprintf(message, _("Can not allocate %.2f MB memory.\n"), (double)(_iSize * sizeof(char*)) / 1.e6);
694         throw ast::InternalError(message);
695     }
696     return pStr;
697 }
698
699 ast::Exp* String::getExp(const Location& loc)
700 {
701     return new ast::StringExp(loc, this);
702 }
703
704 bool String::transpose(InternalType *& out)
705 {
706     return type_traits::transpose(*this, out);
707 }
708
709 }
710