1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT RUN-TIME COMPONENTS                         --
4--                                                                          --
5--      A D A . S T R I N G S . W I D E _ W I D E _ U N B O U N D E D       --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1992-2014, Free Software Foundation, Inc.         --
10--                                                                          --
11-- GNAT is free software;  you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
17--                                                                          --
18-- As a special exception under Section 7 of GPL version 3, you are granted --
19-- additional permissions described in the GCC Runtime Library Exception,   --
20-- version 3.1, as published by the Free Software Foundation.               --
21--                                                                          --
22-- You should have received a copy of the GNU General Public License and    --
23-- a copy of the GCC Runtime Library Exception along with this program;     --
24-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25-- <http://www.gnu.org/licenses/>.                                          --
26--                                                                          --
27-- GNAT was originally developed  by the GNAT team at  New York University. --
28-- Extensive contributions were provided by Ada Core Technologies Inc.      --
29--                                                                          --
30------------------------------------------------------------------------------
31
32with Ada.Strings.Wide_Wide_Search;
33with Ada.Unchecked_Deallocation;
34
35package body Ada.Strings.Wide_Wide_Unbounded is
36
37   use Ada.Strings.Wide_Wide_Maps;
38
39   Growth_Factor : constant := 32;
40   --  The growth factor controls how much extra space is allocated when
41   --  we have to increase the size of an allocated unbounded string. By
42   --  allocating extra space, we avoid the need to reallocate on every
43   --  append, particularly important when a string is built up by repeated
44   --  append operations of small pieces. This is expressed as a factor so
45   --  32 means add 1/32 of the length of the string as growth space.
46
47   Min_Mul_Alloc : constant := Standard'Maximum_Alignment;
48   --  Allocation will be done by a multiple of Min_Mul_Alloc. This causes
49   --  no memory loss as most (all?) malloc implementations are obliged to
50   --  align the returned memory on the maximum alignment as malloc does not
51   --  know the target alignment.
52
53   function Aligned_Max_Length (Max_Length : Natural) return Natural;
54   --  Returns recommended length of the shared string which is greater or
55   --  equal to specified length. Calculation take in sense alignment of
56   --  the allocated memory segments to use memory effectively by
57   --  Append/Insert/etc operations.
58
59   ---------
60   -- "&" --
61   ---------
62
63   function "&"
64     (Left  : Unbounded_Wide_Wide_String;
65      Right : Unbounded_Wide_Wide_String) return Unbounded_Wide_Wide_String
66   is
67      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
68      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
69      DL : constant Natural := LR.Last + RR.Last;
70      DR : Shared_Wide_Wide_String_Access;
71
72   begin
73      --  Result is an empty string, reuse shared empty string
74
75      if DL = 0 then
76         Reference (Empty_Shared_Wide_Wide_String'Access);
77         DR := Empty_Shared_Wide_Wide_String'Access;
78
79      --  Left string is empty, return Rigth string
80
81      elsif LR.Last = 0 then
82         Reference (RR);
83         DR := RR;
84
85      --  Right string is empty, return Left string
86
87      elsif RR.Last = 0 then
88         Reference (LR);
89         DR := LR;
90
91      --  Overwise, allocate new shared string and fill data
92
93      else
94         DR := Allocate (DL);
95         DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last);
96         DR.Data (LR.Last + 1 .. DL) := RR.Data (1 .. RR.Last);
97         DR.Last := DL;
98      end if;
99
100      return (AF.Controlled with Reference => DR);
101   end "&";
102
103   function "&"
104     (Left  : Unbounded_Wide_Wide_String;
105      Right : Wide_Wide_String) return Unbounded_Wide_Wide_String
106   is
107      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
108      DL : constant Natural := LR.Last + Right'Length;
109      DR : Shared_Wide_Wide_String_Access;
110
111   begin
112      --  Result is an empty string, reuse shared empty string
113
114      if DL = 0 then
115         Reference (Empty_Shared_Wide_Wide_String'Access);
116         DR := Empty_Shared_Wide_Wide_String'Access;
117
118      --  Right is an empty string, return Left string
119
120      elsif Right'Length = 0 then
121         Reference (LR);
122         DR := LR;
123
124      --  Otherwise, allocate new shared string and fill it
125
126      else
127         DR := Allocate (DL);
128         DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last);
129         DR.Data (LR.Last + 1 .. DL) := Right;
130         DR.Last := DL;
131      end if;
132
133      return (AF.Controlled with Reference => DR);
134   end "&";
135
136   function "&"
137     (Left  : Wide_Wide_String;
138      Right : Unbounded_Wide_Wide_String) return Unbounded_Wide_Wide_String
139   is
140      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
141      DL : constant Natural := Left'Length + RR.Last;
142      DR : Shared_Wide_Wide_String_Access;
143
144   begin
145      --  Result is an empty string, reuse shared one
146
147      if DL = 0 then
148         Reference (Empty_Shared_Wide_Wide_String'Access);
149         DR := Empty_Shared_Wide_Wide_String'Access;
150
151      --  Left is empty string, return Right string
152
153      elsif Left'Length = 0 then
154         Reference (RR);
155         DR := RR;
156
157      --  Otherwise, allocate new shared string and fill it
158
159      else
160         DR := Allocate (DL);
161         DR.Data (1 .. Left'Length) := Left;
162         DR.Data (Left'Length + 1 .. DL) := RR.Data (1 .. RR.Last);
163         DR.Last := DL;
164      end if;
165
166      return (AF.Controlled with Reference => DR);
167   end "&";
168
169   function "&"
170     (Left  : Unbounded_Wide_Wide_String;
171      Right : Wide_Wide_Character) return Unbounded_Wide_Wide_String
172   is
173      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
174      DL : constant Natural := LR.Last + 1;
175      DR : Shared_Wide_Wide_String_Access;
176
177   begin
178      DR := Allocate (DL);
179      DR.Data (1 .. LR.Last) := LR.Data (1 .. LR.Last);
180      DR.Data (DL) := Right;
181      DR.Last := DL;
182
183      return (AF.Controlled with Reference => DR);
184   end "&";
185
186   function "&"
187     (Left  : Wide_Wide_Character;
188      Right : Unbounded_Wide_Wide_String) return Unbounded_Wide_Wide_String
189   is
190      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
191      DL : constant Natural := 1 + RR.Last;
192      DR : Shared_Wide_Wide_String_Access;
193
194   begin
195      DR := Allocate (DL);
196      DR.Data (1) := Left;
197      DR.Data (2 .. DL) := RR.Data (1 .. RR.Last);
198      DR.Last := DL;
199
200      return (AF.Controlled with Reference => DR);
201   end "&";
202
203   ---------
204   -- "*" --
205   ---------
206
207   function "*"
208     (Left  : Natural;
209      Right : Wide_Wide_Character) return Unbounded_Wide_Wide_String
210   is
211      DR : Shared_Wide_Wide_String_Access;
212
213   begin
214      --  Result is an empty string, reuse shared empty string
215
216      if Left = 0 then
217         Reference (Empty_Shared_Wide_Wide_String'Access);
218         DR := Empty_Shared_Wide_Wide_String'Access;
219
220      --  Otherwise, allocate new shared string and fill it
221
222      else
223         DR := Allocate (Left);
224
225         for J in 1 .. Left loop
226            DR.Data (J) := Right;
227         end loop;
228
229         DR.Last := Left;
230      end if;
231
232      return (AF.Controlled with Reference => DR);
233   end "*";
234
235   function "*"
236     (Left  : Natural;
237      Right : Wide_Wide_String) return Unbounded_Wide_Wide_String
238   is
239      DL : constant Natural := Left * Right'Length;
240      DR : Shared_Wide_Wide_String_Access;
241      K  : Positive;
242
243   begin
244      --  Result is an empty string, reuse shared empty string
245
246      if DL = 0 then
247         Reference (Empty_Shared_Wide_Wide_String'Access);
248         DR := Empty_Shared_Wide_Wide_String'Access;
249
250      --  Otherwise, allocate new shared string and fill it
251
252      else
253         DR := Allocate (DL);
254         K := 1;
255
256         for J in 1 .. Left loop
257            DR.Data (K .. K + Right'Length - 1) := Right;
258            K := K + Right'Length;
259         end loop;
260
261         DR.Last := DL;
262      end if;
263
264      return (AF.Controlled with Reference => DR);
265   end "*";
266
267   function "*"
268     (Left  : Natural;
269      Right : Unbounded_Wide_Wide_String) return Unbounded_Wide_Wide_String
270   is
271      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
272      DL : constant Natural := Left * RR.Last;
273      DR : Shared_Wide_Wide_String_Access;
274      K  : Positive;
275
276   begin
277      --  Result is an empty string, reuse shared empty string
278
279      if DL = 0 then
280         Reference (Empty_Shared_Wide_Wide_String'Access);
281         DR := Empty_Shared_Wide_Wide_String'Access;
282
283      --  Coefficient is one, just return string itself
284
285      elsif Left = 1 then
286         Reference (RR);
287         DR := RR;
288
289      --  Otherwise, allocate new shared string and fill it
290
291      else
292         DR := Allocate (DL);
293         K := 1;
294
295         for J in 1 .. Left loop
296            DR.Data (K .. K + RR.Last - 1) := RR.Data (1 .. RR.Last);
297            K := K + RR.Last;
298         end loop;
299
300         DR.Last := DL;
301      end if;
302
303      return (AF.Controlled with Reference => DR);
304   end "*";
305
306   ---------
307   -- "<" --
308   ---------
309
310   function "<"
311     (Left  : Unbounded_Wide_Wide_String;
312      Right : Unbounded_Wide_Wide_String) return Boolean
313   is
314      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
315      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
316   begin
317      return LR.Data (1 .. LR.Last) < RR.Data (1 .. RR.Last);
318   end "<";
319
320   function "<"
321     (Left  : Unbounded_Wide_Wide_String;
322      Right : Wide_Wide_String) return Boolean
323   is
324      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
325   begin
326      return LR.Data (1 .. LR.Last) < Right;
327   end "<";
328
329   function "<"
330     (Left  : Wide_Wide_String;
331      Right : Unbounded_Wide_Wide_String) return Boolean
332   is
333      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
334   begin
335      return Left < RR.Data (1 .. RR.Last);
336   end "<";
337
338   ----------
339   -- "<=" --
340   ----------
341
342   function "<="
343     (Left  : Unbounded_Wide_Wide_String;
344      Right : Unbounded_Wide_Wide_String) return Boolean
345   is
346      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
347      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
348
349   begin
350      --  LR = RR means two strings shares shared string, thus they are equal
351
352      return LR = RR or else LR.Data (1 .. LR.Last) <= RR.Data (1 .. RR.Last);
353   end "<=";
354
355   function "<="
356     (Left  : Unbounded_Wide_Wide_String;
357      Right : Wide_Wide_String) return Boolean
358   is
359      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
360   begin
361      return LR.Data (1 .. LR.Last) <= Right;
362   end "<=";
363
364   function "<="
365     (Left  : Wide_Wide_String;
366      Right : Unbounded_Wide_Wide_String) return Boolean
367   is
368      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
369   begin
370      return Left <= RR.Data (1 .. RR.Last);
371   end "<=";
372
373   ---------
374   -- "=" --
375   ---------
376
377   function "="
378     (Left  : Unbounded_Wide_Wide_String;
379      Right : Unbounded_Wide_Wide_String) return Boolean
380   is
381      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
382      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
383
384   begin
385      return LR = RR or else LR.Data (1 .. LR.Last) = RR.Data (1 .. RR.Last);
386      --  LR = RR means two strings shares shared string, thus they are equal
387   end "=";
388
389   function "="
390     (Left  : Unbounded_Wide_Wide_String;
391      Right : Wide_Wide_String) return Boolean
392   is
393      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
394   begin
395      return LR.Data (1 .. LR.Last) = Right;
396   end "=";
397
398   function "="
399     (Left  : Wide_Wide_String;
400      Right : Unbounded_Wide_Wide_String) return Boolean
401   is
402      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
403   begin
404      return Left = RR.Data (1 .. RR.Last);
405   end "=";
406
407   ---------
408   -- ">" --
409   ---------
410
411   function ">"
412     (Left  : Unbounded_Wide_Wide_String;
413      Right : Unbounded_Wide_Wide_String) return Boolean
414   is
415      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
416      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
417   begin
418      return LR.Data (1 .. LR.Last) > RR.Data (1 .. RR.Last);
419   end ">";
420
421   function ">"
422     (Left  : Unbounded_Wide_Wide_String;
423      Right : Wide_Wide_String) return Boolean
424   is
425      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
426   begin
427      return LR.Data (1 .. LR.Last) > Right;
428   end ">";
429
430   function ">"
431     (Left  : Wide_Wide_String;
432      Right : Unbounded_Wide_Wide_String) return Boolean
433   is
434      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
435   begin
436      return Left > RR.Data (1 .. RR.Last);
437   end ">";
438
439   ----------
440   -- ">=" --
441   ----------
442
443   function ">="
444     (Left  : Unbounded_Wide_Wide_String;
445      Right : Unbounded_Wide_Wide_String) return Boolean
446   is
447      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
448      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
449
450   begin
451      --  LR = RR means two strings shares shared string, thus they are equal
452
453      return LR = RR or else LR.Data (1 .. LR.Last) >= RR.Data (1 .. RR.Last);
454   end ">=";
455
456   function ">="
457     (Left  : Unbounded_Wide_Wide_String;
458      Right : Wide_Wide_String) return Boolean
459   is
460      LR : constant Shared_Wide_Wide_String_Access := Left.Reference;
461   begin
462      return LR.Data (1 .. LR.Last) >= Right;
463   end ">=";
464
465   function ">="
466     (Left  : Wide_Wide_String;
467      Right : Unbounded_Wide_Wide_String) return Boolean
468   is
469      RR : constant Shared_Wide_Wide_String_Access := Right.Reference;
470   begin
471      return Left >= RR.Data (1 .. RR.Last);
472   end ">=";
473
474   ------------
475   -- Adjust --
476   ------------
477
478   procedure Adjust (Object : in out Unbounded_Wide_Wide_String) is
479   begin
480      Reference (Object.Reference);
481   end Adjust;
482
483   ------------------------
484   -- Aligned_Max_Length --
485   ------------------------
486
487   function Aligned_Max_Length (Max_Length : Natural) return Natural is
488      Static_Size  : constant Natural :=
489        Empty_Shared_Wide_Wide_String'Size / Standard'Storage_Unit;
490      --  Total size of all static components
491
492      Element_Size : constant Natural :=
493        Wide_Wide_Character'Size / Standard'Storage_Unit;
494
495   begin
496      return
497        (((Static_Size + Max_Length * Element_Size - 1) / Min_Mul_Alloc + 2)
498          * Min_Mul_Alloc - Static_Size) / Element_Size;
499   end Aligned_Max_Length;
500
501   --------------
502   -- Allocate --
503   --------------
504
505   function Allocate
506     (Max_Length : Natural) return Shared_Wide_Wide_String_Access is
507   begin
508      --  Empty string requested, return shared empty string
509
510      if Max_Length = 0 then
511         Reference (Empty_Shared_Wide_Wide_String'Access);
512         return Empty_Shared_Wide_Wide_String'Access;
513
514      --  Otherwise, allocate requested space (and probably some more room)
515
516      else
517         return new Shared_Wide_Wide_String (Aligned_Max_Length (Max_Length));
518      end if;
519   end Allocate;
520
521   ------------
522   -- Append --
523   ------------
524
525   procedure Append
526     (Source   : in out Unbounded_Wide_Wide_String;
527      New_Item : Unbounded_Wide_Wide_String)
528   is
529      SR  : constant Shared_Wide_Wide_String_Access := Source.Reference;
530      NR  : constant Shared_Wide_Wide_String_Access := New_Item.Reference;
531      DL  : constant Natural              := SR.Last + NR.Last;
532      DR  : Shared_Wide_Wide_String_Access;
533
534   begin
535      --  Source is an empty string, reuse New_Item data
536
537      if SR.Last = 0 then
538         Reference (NR);
539         Source.Reference := NR;
540         Unreference (SR);
541
542      --  New_Item is empty string, nothing to do
543
544      elsif NR.Last = 0 then
545         null;
546
547      --  Try to reuse existent shared string
548
549      elsif Can_Be_Reused (SR, DL) then
550         SR.Data (SR.Last + 1 .. DL) := NR.Data (1 .. NR.Last);
551         SR.Last := DL;
552
553      --  Otherwise, allocate new one and fill it
554
555      else
556         DR := Allocate (DL + DL / Growth_Factor);
557         DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
558         DR.Data (SR.Last + 1 .. DL) := NR.Data (1 .. NR.Last);
559         DR.Last := DL;
560         Source.Reference := DR;
561         Unreference (SR);
562      end if;
563   end Append;
564
565   procedure Append
566     (Source   : in out Unbounded_Wide_Wide_String;
567      New_Item : Wide_Wide_String)
568   is
569      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
570      DL : constant Natural := SR.Last + New_Item'Length;
571      DR : Shared_Wide_Wide_String_Access;
572
573   begin
574      --  New_Item is an empty string, nothing to do
575
576      if New_Item'Length = 0 then
577         null;
578
579      --  Try to reuse existing shared string
580
581      elsif Can_Be_Reused (SR, DL) then
582         SR.Data (SR.Last + 1 .. DL) := New_Item;
583         SR.Last := DL;
584
585      --  Otherwise, allocate new one and fill it
586
587      else
588         DR := Allocate (DL + DL / Growth_Factor);
589         DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
590         DR.Data (SR.Last + 1 .. DL) := New_Item;
591         DR.Last := DL;
592         Source.Reference := DR;
593         Unreference (SR);
594      end if;
595   end Append;
596
597   procedure Append
598     (Source   : in out Unbounded_Wide_Wide_String;
599      New_Item : Wide_Wide_Character)
600   is
601      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
602      DL : constant Natural := SR.Last + 1;
603      DR : Shared_Wide_Wide_String_Access;
604
605   begin
606      --  Try to reuse existing shared string
607
608      if Can_Be_Reused (SR, SR.Last + 1) then
609         SR.Data (SR.Last + 1) := New_Item;
610         SR.Last := SR.Last + 1;
611
612      --  Otherwise, allocate new one and fill it
613
614      else
615         DR := Allocate (DL + DL / Growth_Factor);
616         DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
617         DR.Data (DL) := New_Item;
618         DR.Last := DL;
619         Source.Reference := DR;
620         Unreference (SR);
621      end if;
622   end Append;
623
624   -------------------
625   -- Can_Be_Reused --
626   -------------------
627
628   function Can_Be_Reused
629     (Item   : Shared_Wide_Wide_String_Access;
630      Length : Natural) return Boolean is
631   begin
632      return
633        System.Atomic_Counters.Is_One (Item.Counter)
634          and then Item.Max_Length >= Length
635          and then Item.Max_Length <=
636                     Aligned_Max_Length (Length + Length / Growth_Factor);
637   end Can_Be_Reused;
638
639   -----------
640   -- Count --
641   -----------
642
643   function Count
644     (Source  : Unbounded_Wide_Wide_String;
645      Pattern : Wide_Wide_String;
646      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping :=
647        Wide_Wide_Maps.Identity) return Natural
648   is
649      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
650   begin
651      return Wide_Wide_Search.Count (SR.Data (1 .. SR.Last), Pattern, Mapping);
652   end Count;
653
654   function Count
655     (Source  : Unbounded_Wide_Wide_String;
656      Pattern : Wide_Wide_String;
657      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function)
658      return Natural
659   is
660      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
661   begin
662      return Wide_Wide_Search.Count (SR.Data (1 .. SR.Last), Pattern, Mapping);
663   end Count;
664
665   function Count
666     (Source : Unbounded_Wide_Wide_String;
667      Set    : Wide_Wide_Maps.Wide_Wide_Character_Set) return Natural
668   is
669      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
670   begin
671      return Wide_Wide_Search.Count (SR.Data (1 .. SR.Last), Set);
672   end Count;
673
674   ------------
675   -- Delete --
676   ------------
677
678   function Delete
679     (Source  : Unbounded_Wide_Wide_String;
680      From    : Positive;
681      Through : Natural) return Unbounded_Wide_Wide_String
682   is
683      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
684      DL : Natural;
685      DR : Shared_Wide_Wide_String_Access;
686
687   begin
688      --  Empty slice is deleted, use the same shared string
689
690      if From > Through then
691         Reference (SR);
692         DR := SR;
693
694      --  Index is out of range
695
696      elsif Through > SR.Last then
697         raise Index_Error;
698
699      --  Compute size of the result
700
701      else
702         DL := SR.Last - (Through - From + 1);
703
704         --  Result is an empty string, reuse shared empty string
705
706         if DL = 0 then
707            Reference (Empty_Shared_Wide_Wide_String'Access);
708            DR := Empty_Shared_Wide_Wide_String'Access;
709
710         --  Otherwise, allocate new shared string and fill it
711
712         else
713            DR := Allocate (DL);
714            DR.Data (1 .. From - 1) := SR.Data (1 .. From - 1);
715            DR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last);
716            DR.Last := DL;
717         end if;
718      end if;
719
720      return (AF.Controlled with Reference => DR);
721   end Delete;
722
723   procedure Delete
724     (Source  : in out Unbounded_Wide_Wide_String;
725      From    : Positive;
726      Through : Natural)
727   is
728      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
729      DL : Natural;
730      DR : Shared_Wide_Wide_String_Access;
731
732   begin
733      --  Nothing changed, return
734
735      if From > Through then
736         null;
737
738      --  Through is outside of the range
739
740      elsif Through > SR.Last then
741         raise Index_Error;
742
743      else
744         DL := SR.Last - (Through - From + 1);
745
746         --  Result is empty, reuse shared empty string
747
748         if DL = 0 then
749            Reference (Empty_Shared_Wide_Wide_String'Access);
750            Source.Reference := Empty_Shared_Wide_Wide_String'Access;
751            Unreference (SR);
752
753         --  Try to reuse existent shared string
754
755         elsif Can_Be_Reused (SR, DL) then
756            SR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last);
757            SR.Last := DL;
758
759         --  Otherwise, allocate new shared string
760
761         else
762            DR := Allocate (DL);
763            DR.Data (1 .. From - 1) := SR.Data (1 .. From - 1);
764            DR.Data (From .. DL) := SR.Data (Through + 1 .. SR.Last);
765            DR.Last := DL;
766            Source.Reference := DR;
767            Unreference (SR);
768         end if;
769      end if;
770   end Delete;
771
772   -------------
773   -- Element --
774   -------------
775
776   function Element
777     (Source : Unbounded_Wide_Wide_String;
778      Index  : Positive) return Wide_Wide_Character
779   is
780      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
781   begin
782      if Index <= SR.Last then
783         return SR.Data (Index);
784      else
785         raise Index_Error;
786      end if;
787   end Element;
788
789   --------------
790   -- Finalize --
791   --------------
792
793   procedure Finalize (Object : in out Unbounded_Wide_Wide_String) is
794      SR : constant Shared_Wide_Wide_String_Access := Object.Reference;
795
796   begin
797      if SR /= null then
798
799         --  The same controlled object can be finalized several times for
800         --  some reason. As per 7.6.1(24) this should have no ill effect,
801         --  so we need to add a guard for the case of finalizing the same
802         --  object twice.
803
804         Object.Reference := null;
805         Unreference (SR);
806      end if;
807   end Finalize;
808
809   ----------------
810   -- Find_Token --
811   ----------------
812
813   procedure Find_Token
814     (Source : Unbounded_Wide_Wide_String;
815      Set    : Wide_Wide_Maps.Wide_Wide_Character_Set;
816      From   : Positive;
817      Test   : Strings.Membership;
818      First  : out Positive;
819      Last   : out Natural)
820   is
821      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
822   begin
823      Wide_Wide_Search.Find_Token
824        (SR.Data (From .. SR.Last), Set, Test, First, Last);
825   end Find_Token;
826
827   procedure Find_Token
828     (Source : Unbounded_Wide_Wide_String;
829      Set    : Wide_Wide_Maps.Wide_Wide_Character_Set;
830      Test   : Strings.Membership;
831      First  : out Positive;
832      Last   : out Natural)
833   is
834      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
835   begin
836      Wide_Wide_Search.Find_Token
837        (SR.Data (1 .. SR.Last), Set, Test, First, Last);
838   end Find_Token;
839
840   ----------
841   -- Free --
842   ----------
843
844   procedure Free (X : in out Wide_Wide_String_Access) is
845      procedure Deallocate is
846         new Ada.Unchecked_Deallocation
847               (Wide_Wide_String, Wide_Wide_String_Access);
848   begin
849      Deallocate (X);
850   end Free;
851
852   ----------
853   -- Head --
854   ----------
855
856   function Head
857     (Source : Unbounded_Wide_Wide_String;
858      Count  : Natural;
859      Pad    : Wide_Wide_Character := Wide_Wide_Space)
860      return Unbounded_Wide_Wide_String
861   is
862      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
863      DR : Shared_Wide_Wide_String_Access;
864
865   begin
866      --  Result is empty, reuse shared empty string
867
868      if Count = 0 then
869         Reference (Empty_Shared_Wide_Wide_String'Access);
870         DR := Empty_Shared_Wide_Wide_String'Access;
871
872      --  Length of the string is the same as requested, reuse source shared
873      --  string.
874
875      elsif Count = SR.Last then
876         Reference (SR);
877         DR := SR;
878
879      --  Otherwise, allocate new shared string and fill it
880
881      else
882         DR := Allocate (Count);
883
884         --  Length of the source string is more than requested, copy
885         --  corresponding slice.
886
887         if Count < SR.Last then
888            DR.Data (1 .. Count) := SR.Data (1 .. Count);
889
890         --  Length of the source string is less than requested, copy all
891         --  contents and fill others by Pad character.
892
893         else
894            DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
895
896            for J in SR.Last + 1 .. Count loop
897               DR.Data (J) := Pad;
898            end loop;
899         end if;
900
901         DR.Last := Count;
902      end if;
903
904      return (AF.Controlled with Reference => DR);
905   end Head;
906
907   procedure Head
908     (Source : in out Unbounded_Wide_Wide_String;
909      Count  : Natural;
910      Pad    : Wide_Wide_Character := Wide_Wide_Space)
911   is
912      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
913      DR : Shared_Wide_Wide_String_Access;
914
915   begin
916      --  Result is empty, reuse empty shared string
917
918      if Count = 0 then
919         Reference (Empty_Shared_Wide_Wide_String'Access);
920         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
921         Unreference (SR);
922
923      --  Result is same with source string, reuse source shared string
924
925      elsif Count = SR.Last then
926         null;
927
928      --  Try to reuse existent shared string
929
930      elsif Can_Be_Reused (SR, Count) then
931         if Count > SR.Last then
932            for J in SR.Last + 1 .. Count loop
933               SR.Data (J) := Pad;
934            end loop;
935         end if;
936
937         SR.Last := Count;
938
939      --  Otherwise, allocate new shared string and fill it
940
941      else
942         DR := Allocate (Count);
943
944         --  Length of the source string is greater than requested, copy
945         --  corresponding slice.
946
947         if Count < SR.Last then
948            DR.Data (1 .. Count) := SR.Data (1 .. Count);
949
950         --  Length of the source string is less than requested, copy all
951         --  exists data and fill others by Pad character.
952
953         else
954            DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
955
956            for J in SR.Last + 1 .. Count loop
957               DR.Data (J) := Pad;
958            end loop;
959         end if;
960
961         DR.Last := Count;
962         Source.Reference := DR;
963         Unreference (SR);
964      end if;
965   end Head;
966
967   -----------
968   -- Index --
969   -----------
970
971   function Index
972     (Source  : Unbounded_Wide_Wide_String;
973      Pattern : Wide_Wide_String;
974      Going   : Strings.Direction := Strings.Forward;
975      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping :=
976        Wide_Wide_Maps.Identity) return Natural
977   is
978      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
979   begin
980      return Wide_Wide_Search.Index
981        (SR.Data (1 .. SR.Last), Pattern, Going, Mapping);
982   end Index;
983
984   function Index
985     (Source  : Unbounded_Wide_Wide_String;
986      Pattern : Wide_Wide_String;
987      Going   : Direction := Forward;
988      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function)
989      return Natural
990   is
991      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
992   begin
993      return Wide_Wide_Search.Index
994        (SR.Data (1 .. SR.Last), Pattern, Going, Mapping);
995   end Index;
996
997   function Index
998     (Source : Unbounded_Wide_Wide_String;
999      Set    : Wide_Wide_Maps.Wide_Wide_Character_Set;
1000      Test   : Strings.Membership := Strings.Inside;
1001      Going  : Strings.Direction  := Strings.Forward) return Natural
1002   is
1003      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1004   begin
1005      return Wide_Wide_Search.Index (SR.Data (1 .. SR.Last), Set, Test, Going);
1006   end Index;
1007
1008   function Index
1009     (Source  : Unbounded_Wide_Wide_String;
1010      Pattern : Wide_Wide_String;
1011      From    : Positive;
1012      Going   : Direction := Forward;
1013      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping :=
1014        Wide_Wide_Maps.Identity) return Natural
1015   is
1016      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1017   begin
1018      return Wide_Wide_Search.Index
1019        (SR.Data (1 .. SR.Last), Pattern, From, Going, Mapping);
1020   end Index;
1021
1022   function Index
1023     (Source  : Unbounded_Wide_Wide_String;
1024      Pattern : Wide_Wide_String;
1025      From    : Positive;
1026      Going   : Direction := Forward;
1027      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function)
1028      return Natural
1029   is
1030      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1031   begin
1032      return Wide_Wide_Search.Index
1033        (SR.Data (1 .. SR.Last), Pattern, From, Going, Mapping);
1034   end Index;
1035
1036   function Index
1037     (Source  : Unbounded_Wide_Wide_String;
1038      Set     : Wide_Wide_Maps.Wide_Wide_Character_Set;
1039      From    : Positive;
1040      Test    : Membership := Inside;
1041      Going   : Direction := Forward) return Natural
1042   is
1043      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1044   begin
1045      return Wide_Wide_Search.Index
1046        (SR.Data (1 .. SR.Last), Set, From, Test, Going);
1047   end Index;
1048
1049   ---------------------
1050   -- Index_Non_Blank --
1051   ---------------------
1052
1053   function Index_Non_Blank
1054     (Source : Unbounded_Wide_Wide_String;
1055      Going  : Strings.Direction := Strings.Forward) return Natural
1056   is
1057      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1058   begin
1059      return Wide_Wide_Search.Index_Non_Blank (SR.Data (1 .. SR.Last), Going);
1060   end Index_Non_Blank;
1061
1062   function Index_Non_Blank
1063     (Source : Unbounded_Wide_Wide_String;
1064      From   : Positive;
1065      Going  : Direction := Forward) return Natural
1066   is
1067      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1068   begin
1069      return Wide_Wide_Search.Index_Non_Blank
1070        (SR.Data (1 .. SR.Last), From, Going);
1071   end Index_Non_Blank;
1072
1073   ----------------
1074   -- Initialize --
1075   ----------------
1076
1077   procedure Initialize (Object : in out Unbounded_Wide_Wide_String) is
1078   begin
1079      Reference (Object.Reference);
1080   end Initialize;
1081
1082   ------------
1083   -- Insert --
1084   ------------
1085
1086   function Insert
1087     (Source   : Unbounded_Wide_Wide_String;
1088      Before   : Positive;
1089      New_Item : Wide_Wide_String) return Unbounded_Wide_Wide_String
1090   is
1091      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1092      DL : constant Natural := SR.Last + New_Item'Length;
1093      DR : Shared_Wide_Wide_String_Access;
1094
1095   begin
1096      --  Check index first
1097
1098      if Before > SR.Last + 1 then
1099         raise Index_Error;
1100      end if;
1101
1102      --  Result is empty, reuse empty shared string
1103
1104      if DL = 0 then
1105         Reference (Empty_Shared_Wide_Wide_String'Access);
1106         DR := Empty_Shared_Wide_Wide_String'Access;
1107
1108      --  Inserted string is empty, reuse source shared string
1109
1110      elsif New_Item'Length = 0 then
1111         Reference (SR);
1112         DR := SR;
1113
1114      --  Otherwise, allocate new shared string and fill it
1115
1116      else
1117         DR := Allocate (DL + DL / Growth_Factor);
1118         DR.Data (1 .. Before - 1) := SR.Data (1 .. Before - 1);
1119         DR.Data (Before .. Before + New_Item'Length - 1) := New_Item;
1120         DR.Data (Before + New_Item'Length .. DL) :=
1121           SR.Data (Before .. SR.Last);
1122         DR.Last := DL;
1123      end if;
1124
1125      return (AF.Controlled with Reference => DR);
1126   end Insert;
1127
1128   procedure Insert
1129     (Source   : in out Unbounded_Wide_Wide_String;
1130      Before   : Positive;
1131      New_Item : Wide_Wide_String)
1132   is
1133      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1134      DL : constant Natural := SR.Last + New_Item'Length;
1135      DR : Shared_Wide_Wide_String_Access;
1136
1137   begin
1138      --  Check bounds
1139
1140      if Before > SR.Last + 1 then
1141         raise Index_Error;
1142      end if;
1143
1144      --  Result is empty string, reuse empty shared string
1145
1146      if DL = 0 then
1147         Reference (Empty_Shared_Wide_Wide_String'Access);
1148         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1149         Unreference (SR);
1150
1151      --  Inserted string is empty, nothing to do
1152
1153      elsif New_Item'Length = 0 then
1154         null;
1155
1156      --  Try to reuse existent shared string first
1157
1158      elsif Can_Be_Reused (SR, DL) then
1159         SR.Data (Before + New_Item'Length .. DL) :=
1160           SR.Data (Before .. SR.Last);
1161         SR.Data (Before .. Before + New_Item'Length - 1) := New_Item;
1162         SR.Last := DL;
1163
1164      --  Otherwise, allocate new shared string and fill it
1165
1166      else
1167         DR := Allocate (DL + DL / Growth_Factor);
1168         DR.Data (1 .. Before - 1) := SR.Data (1 .. Before - 1);
1169         DR.Data (Before .. Before + New_Item'Length - 1) := New_Item;
1170         DR.Data (Before + New_Item'Length .. DL) :=
1171           SR.Data (Before .. SR.Last);
1172         DR.Last := DL;
1173         Source.Reference := DR;
1174         Unreference (SR);
1175      end if;
1176   end Insert;
1177
1178   ------------
1179   -- Length --
1180   ------------
1181
1182   function Length (Source : Unbounded_Wide_Wide_String) return Natural is
1183   begin
1184      return Source.Reference.Last;
1185   end Length;
1186
1187   ---------------
1188   -- Overwrite --
1189   ---------------
1190
1191   function Overwrite
1192     (Source   : Unbounded_Wide_Wide_String;
1193      Position : Positive;
1194      New_Item : Wide_Wide_String) return Unbounded_Wide_Wide_String
1195   is
1196      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1197      DL : Natural;
1198      DR : Shared_Wide_Wide_String_Access;
1199
1200   begin
1201      --  Check bounds
1202
1203      if Position > SR.Last + 1 then
1204         raise Index_Error;
1205      end if;
1206
1207      DL := Integer'Max (SR.Last, Position + New_Item'Length - 1);
1208
1209      --  Result is empty string, reuse empty shared string
1210
1211      if DL = 0 then
1212         Reference (Empty_Shared_Wide_Wide_String'Access);
1213         DR := Empty_Shared_Wide_Wide_String'Access;
1214
1215      --  Result is same with source string, reuse source shared string
1216
1217      elsif New_Item'Length = 0 then
1218         Reference (SR);
1219         DR := SR;
1220
1221      --  Otherwise, allocate new shared string and fill it
1222
1223      else
1224         DR := Allocate (DL);
1225         DR.Data (1 .. Position - 1) := SR.Data (1 .. Position - 1);
1226         DR.Data (Position .. Position + New_Item'Length - 1) := New_Item;
1227         DR.Data (Position + New_Item'Length .. DL) :=
1228           SR.Data (Position + New_Item'Length .. SR.Last);
1229         DR.Last := DL;
1230      end if;
1231
1232      return (AF.Controlled with Reference => DR);
1233   end Overwrite;
1234
1235   procedure Overwrite
1236     (Source    : in out Unbounded_Wide_Wide_String;
1237      Position  : Positive;
1238      New_Item  : Wide_Wide_String)
1239   is
1240      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1241      DL : Natural;
1242      DR : Shared_Wide_Wide_String_Access;
1243
1244   begin
1245      --  Bounds check
1246
1247      if Position > SR.Last + 1 then
1248         raise Index_Error;
1249      end if;
1250
1251      DL := Integer'Max (SR.Last, Position + New_Item'Length - 1);
1252
1253      --  Result is empty string, reuse empty shared string
1254
1255      if DL = 0 then
1256         Reference (Empty_Shared_Wide_Wide_String'Access);
1257         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1258         Unreference (SR);
1259
1260      --  String unchanged, nothing to do
1261
1262      elsif New_Item'Length = 0 then
1263         null;
1264
1265      --  Try to reuse existent shared string
1266
1267      elsif Can_Be_Reused (SR, DL) then
1268         SR.Data (Position .. Position + New_Item'Length - 1) := New_Item;
1269         SR.Last := DL;
1270
1271      --  Otherwise allocate new shared string and fill it
1272
1273      else
1274         DR := Allocate (DL);
1275         DR.Data (1 .. Position - 1) := SR.Data (1 .. Position - 1);
1276         DR.Data (Position .. Position + New_Item'Length - 1) := New_Item;
1277         DR.Data (Position + New_Item'Length .. DL) :=
1278           SR.Data (Position + New_Item'Length .. SR.Last);
1279         DR.Last := DL;
1280         Source.Reference := DR;
1281         Unreference (SR);
1282      end if;
1283   end Overwrite;
1284
1285   ---------------
1286   -- Reference --
1287   ---------------
1288
1289   procedure Reference (Item : not null Shared_Wide_Wide_String_Access) is
1290   begin
1291      System.Atomic_Counters.Increment (Item.Counter);
1292   end Reference;
1293
1294   ---------------------
1295   -- Replace_Element --
1296   ---------------------
1297
1298   procedure Replace_Element
1299     (Source : in out Unbounded_Wide_Wide_String;
1300      Index  : Positive;
1301      By     : Wide_Wide_Character)
1302   is
1303      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1304      DR : Shared_Wide_Wide_String_Access;
1305
1306   begin
1307      --  Bounds check
1308
1309      if Index <= SR.Last then
1310
1311         --  Try to reuse existent shared string
1312
1313         if Can_Be_Reused (SR, SR.Last) then
1314            SR.Data (Index) := By;
1315
1316         --  Otherwise allocate new shared string and fill it
1317
1318         else
1319            DR := Allocate (SR.Last);
1320            DR.Data (1 .. SR.Last) := SR.Data (1 .. SR.Last);
1321            DR.Data (Index) := By;
1322            DR.Last := SR.Last;
1323            Source.Reference := DR;
1324            Unreference (SR);
1325         end if;
1326
1327      else
1328         raise Index_Error;
1329      end if;
1330   end Replace_Element;
1331
1332   -------------------
1333   -- Replace_Slice --
1334   -------------------
1335
1336   function Replace_Slice
1337     (Source : Unbounded_Wide_Wide_String;
1338      Low    : Positive;
1339      High   : Natural;
1340      By     : Wide_Wide_String) return Unbounded_Wide_Wide_String
1341   is
1342      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1343      DL : Natural;
1344      DR : Shared_Wide_Wide_String_Access;
1345
1346   begin
1347      --  Check bounds
1348
1349      if Low > SR.Last + 1 then
1350         raise Index_Error;
1351      end if;
1352
1353      --  Do replace operation when removed slice is not empty
1354
1355      if High >= Low then
1356         DL := By'Length + SR.Last + Low - Integer'Min (High, SR.Last) - 1;
1357         --  This is the number of characters remaining in the string after
1358         --  replacing the slice.
1359
1360         --  Result is empty string, reuse empty shared string
1361
1362         if DL = 0 then
1363            Reference (Empty_Shared_Wide_Wide_String'Access);
1364            DR := Empty_Shared_Wide_Wide_String'Access;
1365
1366         --  Otherwise allocate new shared string and fill it
1367
1368         else
1369            DR := Allocate (DL);
1370            DR.Data (1 .. Low - 1) := SR.Data (1 .. Low - 1);
1371            DR.Data (Low .. Low + By'Length - 1) := By;
1372            DR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last);
1373            DR.Last := DL;
1374         end if;
1375
1376         return (AF.Controlled with Reference => DR);
1377
1378      --  Otherwise just insert string
1379
1380      else
1381         return Insert (Source, Low, By);
1382      end if;
1383   end Replace_Slice;
1384
1385   procedure Replace_Slice
1386     (Source : in out Unbounded_Wide_Wide_String;
1387      Low    : Positive;
1388      High   : Natural;
1389      By     : Wide_Wide_String)
1390   is
1391      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1392      DL : Natural;
1393      DR : Shared_Wide_Wide_String_Access;
1394
1395   begin
1396      --  Bounds check
1397
1398      if Low > SR.Last + 1 then
1399         raise Index_Error;
1400      end if;
1401
1402      --  Do replace operation only when replaced slice is not empty
1403
1404      if High >= Low then
1405         DL := By'Length + SR.Last + Low - Integer'Min (High, SR.Last) - 1;
1406         --  This is the number of characters remaining in the string after
1407         --  replacing the slice.
1408
1409         --  Result is empty string, reuse empty shared string
1410
1411         if DL = 0 then
1412            Reference (Empty_Shared_Wide_Wide_String'Access);
1413            Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1414            Unreference (SR);
1415
1416         --  Try to reuse existent shared string
1417
1418         elsif Can_Be_Reused (SR, DL) then
1419            SR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last);
1420            SR.Data (Low .. Low + By'Length - 1) := By;
1421            SR.Last := DL;
1422
1423         --  Otherwise allocate new shared string and fill it
1424
1425         else
1426            DR := Allocate (DL);
1427            DR.Data (1 .. Low - 1) := SR.Data (1 .. Low - 1);
1428            DR.Data (Low .. Low + By'Length - 1) := By;
1429            DR.Data (Low + By'Length .. DL) := SR.Data (High + 1 .. SR.Last);
1430            DR.Last := DL;
1431            Source.Reference := DR;
1432            Unreference (SR);
1433         end if;
1434
1435      --  Otherwise just insert item
1436
1437      else
1438         Insert (Source, Low, By);
1439      end if;
1440   end Replace_Slice;
1441
1442   -------------------------------
1443   -- Set_Unbounded_Wide_Wide_String --
1444   -------------------------------
1445
1446   procedure Set_Unbounded_Wide_Wide_String
1447     (Target : out Unbounded_Wide_Wide_String;
1448      Source : Wide_Wide_String)
1449   is
1450      TR : constant Shared_Wide_Wide_String_Access := Target.Reference;
1451      DR : Shared_Wide_Wide_String_Access;
1452
1453   begin
1454      --  In case of empty string, reuse empty shared string
1455
1456      if Source'Length = 0 then
1457         Reference (Empty_Shared_Wide_Wide_String'Access);
1458         Target.Reference := Empty_Shared_Wide_Wide_String'Access;
1459
1460      else
1461         --  Try to reuse existent shared string
1462
1463         if Can_Be_Reused (TR, Source'Length) then
1464            Reference (TR);
1465            DR := TR;
1466
1467         --  Otherwise allocate new shared string
1468
1469         else
1470            DR := Allocate (Source'Length);
1471            Target.Reference := DR;
1472         end if;
1473
1474         DR.Data (1 .. Source'Length) := Source;
1475         DR.Last := Source'Length;
1476      end if;
1477
1478      Unreference (TR);
1479   end Set_Unbounded_Wide_Wide_String;
1480
1481   -----------
1482   -- Slice --
1483   -----------
1484
1485   function Slice
1486     (Source : Unbounded_Wide_Wide_String;
1487      Low    : Positive;
1488      High   : Natural) return Wide_Wide_String
1489   is
1490      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1491
1492   begin
1493      --  Note: test of High > Length is in accordance with AI95-00128
1494
1495      if Low > SR.Last + 1 or else High > SR.Last then
1496         raise Index_Error;
1497
1498      else
1499         return SR.Data (Low .. High);
1500      end if;
1501   end Slice;
1502
1503   ----------
1504   -- Tail --
1505   ----------
1506
1507   function Tail
1508     (Source : Unbounded_Wide_Wide_String;
1509      Count  : Natural;
1510      Pad    : Wide_Wide_Character := Wide_Wide_Space)
1511      return Unbounded_Wide_Wide_String
1512   is
1513      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1514      DR : Shared_Wide_Wide_String_Access;
1515
1516   begin
1517      --  For empty result reuse empty shared string
1518
1519      if Count = 0 then
1520         Reference (Empty_Shared_Wide_Wide_String'Access);
1521         DR := Empty_Shared_Wide_Wide_String'Access;
1522
1523      --  Result is hole source string, reuse source shared string
1524
1525      elsif Count = SR.Last then
1526         Reference (SR);
1527         DR := SR;
1528
1529      --  Otherwise allocate new shared string and fill it
1530
1531      else
1532         DR := Allocate (Count);
1533
1534         if Count < SR.Last then
1535            DR.Data (1 .. Count) := SR.Data (SR.Last - Count + 1 .. SR.Last);
1536
1537         else
1538            for J in 1 .. Count - SR.Last loop
1539               DR.Data (J) := Pad;
1540            end loop;
1541
1542            DR.Data (Count - SR.Last + 1 .. Count) := SR.Data (1 .. SR.Last);
1543         end if;
1544
1545         DR.Last := Count;
1546      end if;
1547
1548      return (AF.Controlled with Reference => DR);
1549   end Tail;
1550
1551   procedure Tail
1552     (Source : in out Unbounded_Wide_Wide_String;
1553      Count  : Natural;
1554      Pad    : Wide_Wide_Character := Wide_Wide_Space)
1555   is
1556      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1557      DR : Shared_Wide_Wide_String_Access;
1558
1559      procedure Common
1560        (SR    : Shared_Wide_Wide_String_Access;
1561         DR    : Shared_Wide_Wide_String_Access;
1562         Count : Natural);
1563      --  Common code of tail computation. SR/DR can point to the same object
1564
1565      ------------
1566      -- Common --
1567      ------------
1568
1569      procedure Common
1570        (SR    : Shared_Wide_Wide_String_Access;
1571         DR    : Shared_Wide_Wide_String_Access;
1572         Count : Natural) is
1573      begin
1574         if Count < SR.Last then
1575            DR.Data (1 .. Count) := SR.Data (SR.Last - Count + 1 .. SR.Last);
1576
1577         else
1578            DR.Data (Count - SR.Last + 1 .. Count) := SR.Data (1 .. SR.Last);
1579
1580            for J in 1 .. Count - SR.Last loop
1581               DR.Data (J) := Pad;
1582            end loop;
1583         end if;
1584
1585         DR.Last := Count;
1586      end Common;
1587
1588   begin
1589      --  Result is empty string, reuse empty shared string
1590
1591      if Count = 0 then
1592         Reference (Empty_Shared_Wide_Wide_String'Access);
1593         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1594         Unreference (SR);
1595
1596      --  Length of the result is the same with length of the source string,
1597      --  reuse source shared string.
1598
1599      elsif Count = SR.Last then
1600         null;
1601
1602      --  Try to reuse existent shared string
1603
1604      elsif Can_Be_Reused (SR, Count) then
1605         Common (SR, SR, Count);
1606
1607      --  Otherwise allocate new shared string and fill it
1608
1609      else
1610         DR := Allocate (Count);
1611         Common (SR, DR, Count);
1612         Source.Reference := DR;
1613         Unreference (SR);
1614      end if;
1615   end Tail;
1616
1617   -------------------------
1618   -- To_Wide_Wide_String --
1619   -------------------------
1620
1621   function To_Wide_Wide_String
1622     (Source : Unbounded_Wide_Wide_String) return Wide_Wide_String is
1623   begin
1624      return Source.Reference.Data (1 .. Source.Reference.Last);
1625   end To_Wide_Wide_String;
1626
1627   -----------------------------------
1628   -- To_Unbounded_Wide_Wide_String --
1629   -----------------------------------
1630
1631   function To_Unbounded_Wide_Wide_String
1632     (Source : Wide_Wide_String) return Unbounded_Wide_Wide_String
1633   is
1634      DR : Shared_Wide_Wide_String_Access;
1635
1636   begin
1637      if Source'Length = 0 then
1638         Reference (Empty_Shared_Wide_Wide_String'Access);
1639         DR := Empty_Shared_Wide_Wide_String'Access;
1640
1641      else
1642         DR := Allocate (Source'Length);
1643         DR.Data (1 .. Source'Length) := Source;
1644         DR.Last := Source'Length;
1645      end if;
1646
1647      return (AF.Controlled with Reference => DR);
1648   end To_Unbounded_Wide_Wide_String;
1649
1650   function To_Unbounded_Wide_Wide_String
1651     (Length : Natural) return Unbounded_Wide_Wide_String
1652   is
1653      DR : Shared_Wide_Wide_String_Access;
1654
1655   begin
1656      if Length = 0 then
1657         Reference (Empty_Shared_Wide_Wide_String'Access);
1658         DR := Empty_Shared_Wide_Wide_String'Access;
1659
1660      else
1661         DR := Allocate (Length);
1662         DR.Last := Length;
1663      end if;
1664
1665      return (AF.Controlled with Reference => DR);
1666   end To_Unbounded_Wide_Wide_String;
1667
1668   ---------------
1669   -- Translate --
1670   ---------------
1671
1672   function Translate
1673     (Source  : Unbounded_Wide_Wide_String;
1674      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping)
1675      return Unbounded_Wide_Wide_String
1676   is
1677      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1678      DR : Shared_Wide_Wide_String_Access;
1679
1680   begin
1681      --  Nothing to translate, reuse empty shared string
1682
1683      if SR.Last = 0 then
1684         Reference (Empty_Shared_Wide_Wide_String'Access);
1685         DR := Empty_Shared_Wide_Wide_String'Access;
1686
1687      --  Otherwise, allocate new shared string and fill it
1688
1689      else
1690         DR := Allocate (SR.Last);
1691
1692         for J in 1 .. SR.Last loop
1693            DR.Data (J) := Value (Mapping, SR.Data (J));
1694         end loop;
1695
1696         DR.Last := SR.Last;
1697      end if;
1698
1699      return (AF.Controlled with Reference => DR);
1700   end Translate;
1701
1702   procedure Translate
1703     (Source  : in out Unbounded_Wide_Wide_String;
1704      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping)
1705   is
1706      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1707      DR : Shared_Wide_Wide_String_Access;
1708
1709   begin
1710      --  Nothing to translate
1711
1712      if SR.Last = 0 then
1713         null;
1714
1715      --  Try to reuse shared string
1716
1717      elsif Can_Be_Reused (SR, SR.Last) then
1718         for J in 1 .. SR.Last loop
1719            SR.Data (J) := Value (Mapping, SR.Data (J));
1720         end loop;
1721
1722      --  Otherwise, allocate new shared string
1723
1724      else
1725         DR := Allocate (SR.Last);
1726
1727         for J in 1 .. SR.Last loop
1728            DR.Data (J) := Value (Mapping, SR.Data (J));
1729         end loop;
1730
1731         DR.Last := SR.Last;
1732         Source.Reference := DR;
1733         Unreference (SR);
1734      end if;
1735   end Translate;
1736
1737   function Translate
1738     (Source  : Unbounded_Wide_Wide_String;
1739      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function)
1740      return Unbounded_Wide_Wide_String
1741   is
1742      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1743      DR : Shared_Wide_Wide_String_Access;
1744
1745   begin
1746      --  Nothing to translate, reuse empty shared string
1747
1748      if SR.Last = 0 then
1749         Reference (Empty_Shared_Wide_Wide_String'Access);
1750         DR := Empty_Shared_Wide_Wide_String'Access;
1751
1752      --  Otherwise, allocate new shared string and fill it
1753
1754      else
1755         DR := Allocate (SR.Last);
1756
1757         for J in 1 .. SR.Last loop
1758            DR.Data (J) := Mapping.all (SR.Data (J));
1759         end loop;
1760
1761         DR.Last := SR.Last;
1762      end if;
1763
1764      return (AF.Controlled with Reference => DR);
1765
1766   exception
1767      when others =>
1768         Unreference (DR);
1769
1770         raise;
1771   end Translate;
1772
1773   procedure Translate
1774     (Source  : in out Unbounded_Wide_Wide_String;
1775      Mapping : Wide_Wide_Maps.Wide_Wide_Character_Mapping_Function)
1776   is
1777      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
1778      DR : Shared_Wide_Wide_String_Access;
1779
1780   begin
1781      --  Nothing to translate
1782
1783      if SR.Last = 0 then
1784         null;
1785
1786      --  Try to reuse shared string
1787
1788      elsif Can_Be_Reused (SR, SR.Last) then
1789         for J in 1 .. SR.Last loop
1790            SR.Data (J) := Mapping.all (SR.Data (J));
1791         end loop;
1792
1793      --  Otherwise allocate new shared string and fill it
1794
1795      else
1796         DR := Allocate (SR.Last);
1797
1798         for J in 1 .. SR.Last loop
1799            DR.Data (J) := Mapping.all (SR.Data (J));
1800         end loop;
1801
1802         DR.Last := SR.Last;
1803         Source.Reference := DR;
1804         Unreference (SR);
1805      end if;
1806
1807   exception
1808      when others =>
1809         if DR /= null then
1810            Unreference (DR);
1811         end if;
1812
1813         raise;
1814   end Translate;
1815
1816   ----------
1817   -- Trim --
1818   ----------
1819
1820   function Trim
1821     (Source : Unbounded_Wide_Wide_String;
1822      Side   : Trim_End) return Unbounded_Wide_Wide_String
1823   is
1824      SR   : constant Shared_Wide_Wide_String_Access := Source.Reference;
1825      DL   : Natural;
1826      DR   : Shared_Wide_Wide_String_Access;
1827      Low  : Natural;
1828      High : Natural;
1829
1830   begin
1831      Low := Index_Non_Blank (Source, Forward);
1832
1833      --  All blanks, reuse empty shared string
1834
1835      if Low = 0 then
1836         Reference (Empty_Shared_Wide_Wide_String'Access);
1837         DR := Empty_Shared_Wide_Wide_String'Access;
1838
1839      else
1840         case Side is
1841            when Left =>
1842               High := SR.Last;
1843               DL   := SR.Last - Low + 1;
1844
1845            when Right =>
1846               Low  := 1;
1847               High := Index_Non_Blank (Source, Backward);
1848               DL   := High;
1849
1850            when Both =>
1851               High := Index_Non_Blank (Source, Backward);
1852               DL   := High - Low + 1;
1853         end case;
1854
1855         --  Length of the result is the same as length of the source string,
1856         --  reuse source shared string.
1857
1858         if DL = SR.Last then
1859            Reference (SR);
1860            DR := SR;
1861
1862         --  Otherwise, allocate new shared string
1863
1864         else
1865            DR := Allocate (DL);
1866            DR.Data (1 .. DL) := SR.Data (Low .. High);
1867            DR.Last := DL;
1868         end if;
1869      end if;
1870
1871      return (AF.Controlled with Reference => DR);
1872   end Trim;
1873
1874   procedure Trim
1875     (Source : in out Unbounded_Wide_Wide_String;
1876      Side   : Trim_End)
1877   is
1878      SR   : constant Shared_Wide_Wide_String_Access := Source.Reference;
1879      DL   : Natural;
1880      DR   : Shared_Wide_Wide_String_Access;
1881      Low  : Natural;
1882      High : Natural;
1883
1884   begin
1885      Low := Index_Non_Blank (Source, Forward);
1886
1887      --  All blanks, reuse empty shared string
1888
1889      if Low = 0 then
1890         Reference (Empty_Shared_Wide_Wide_String'Access);
1891         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1892         Unreference (SR);
1893
1894      else
1895         case Side is
1896            when Left =>
1897               High := SR.Last;
1898               DL   := SR.Last - Low + 1;
1899
1900            when Right =>
1901               Low  := 1;
1902               High := Index_Non_Blank (Source, Backward);
1903               DL   := High;
1904
1905            when Both =>
1906               High := Index_Non_Blank (Source, Backward);
1907               DL   := High - Low + 1;
1908         end case;
1909
1910         --  Length of the result is the same as length of the source string,
1911         --  nothing to do.
1912
1913         if DL = SR.Last then
1914            null;
1915
1916         --  Try to reuse existent shared string
1917
1918         elsif Can_Be_Reused (SR, DL) then
1919            SR.Data (1 .. DL) := SR.Data (Low .. High);
1920            SR.Last := DL;
1921
1922         --  Otherwise, allocate new shared string
1923
1924         else
1925            DR := Allocate (DL);
1926            DR.Data (1 .. DL) := SR.Data (Low .. High);
1927            DR.Last := DL;
1928            Source.Reference := DR;
1929            Unreference (SR);
1930         end if;
1931      end if;
1932   end Trim;
1933
1934   function Trim
1935     (Source : Unbounded_Wide_Wide_String;
1936      Left   : Wide_Wide_Maps.Wide_Wide_Character_Set;
1937      Right  : Wide_Wide_Maps.Wide_Wide_Character_Set)
1938      return Unbounded_Wide_Wide_String
1939   is
1940      SR   : constant Shared_Wide_Wide_String_Access := Source.Reference;
1941      DL   : Natural;
1942      DR   : Shared_Wide_Wide_String_Access;
1943      Low  : Natural;
1944      High : Natural;
1945
1946   begin
1947      Low := Index (Source, Left, Outside, Forward);
1948
1949      --  Source includes only characters from Left set, reuse empty shared
1950      --  string.
1951
1952      if Low = 0 then
1953         Reference (Empty_Shared_Wide_Wide_String'Access);
1954         DR := Empty_Shared_Wide_Wide_String'Access;
1955
1956      else
1957         High := Index (Source, Right, Outside, Backward);
1958         DL   := Integer'Max (0, High - Low + 1);
1959
1960         --  Source includes only characters from Right set or result string
1961         --  is empty, reuse empty shared string.
1962
1963         if High = 0 or else DL = 0 then
1964            Reference (Empty_Shared_Wide_Wide_String'Access);
1965            DR := Empty_Shared_Wide_Wide_String'Access;
1966
1967         --  Otherwise, allocate new shared string and fill it
1968
1969         else
1970            DR := Allocate (DL);
1971            DR.Data (1 .. DL) := SR.Data (Low .. High);
1972            DR.Last := DL;
1973         end if;
1974      end if;
1975
1976      return (AF.Controlled with Reference => DR);
1977   end Trim;
1978
1979   procedure Trim
1980     (Source : in out Unbounded_Wide_Wide_String;
1981      Left   : Wide_Wide_Maps.Wide_Wide_Character_Set;
1982      Right  : Wide_Wide_Maps.Wide_Wide_Character_Set)
1983   is
1984      SR   : constant Shared_Wide_Wide_String_Access := Source.Reference;
1985      DL   : Natural;
1986      DR   : Shared_Wide_Wide_String_Access;
1987      Low  : Natural;
1988      High : Natural;
1989
1990   begin
1991      Low := Index (Source, Left, Outside, Forward);
1992
1993      --  Source includes only characters from Left set, reuse empty shared
1994      --  string.
1995
1996      if Low = 0 then
1997         Reference (Empty_Shared_Wide_Wide_String'Access);
1998         Source.Reference := Empty_Shared_Wide_Wide_String'Access;
1999         Unreference (SR);
2000
2001      else
2002         High := Index (Source, Right, Outside, Backward);
2003         DL   := Integer'Max (0, High - Low + 1);
2004
2005         --  Source includes only characters from Right set or result string
2006         --  is empty, reuse empty shared string.
2007
2008         if High = 0 or else DL = 0 then
2009            Reference (Empty_Shared_Wide_Wide_String'Access);
2010            Source.Reference := Empty_Shared_Wide_Wide_String'Access;
2011            Unreference (SR);
2012
2013         --  Try to reuse existent shared string
2014
2015         elsif Can_Be_Reused (SR, DL) then
2016            SR.Data (1 .. DL) := SR.Data (Low .. High);
2017            SR.Last := DL;
2018
2019         --  Otherwise, allocate new shared string and fill it
2020
2021         else
2022            DR := Allocate (DL);
2023            DR.Data (1 .. DL) := SR.Data (Low .. High);
2024            DR.Last := DL;
2025            Source.Reference := DR;
2026            Unreference (SR);
2027         end if;
2028      end if;
2029   end Trim;
2030
2031   ---------------------
2032   -- Unbounded_Slice --
2033   ---------------------
2034
2035   function Unbounded_Slice
2036     (Source : Unbounded_Wide_Wide_String;
2037      Low    : Positive;
2038      High   : Natural) return Unbounded_Wide_Wide_String
2039   is
2040      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
2041      DL : Natural;
2042      DR : Shared_Wide_Wide_String_Access;
2043
2044   begin
2045      --  Check bounds
2046
2047      if Low > SR.Last + 1 or else High > SR.Last then
2048         raise Index_Error;
2049
2050      --  Result is empty slice, reuse empty shared string
2051
2052      elsif Low > High then
2053         Reference (Empty_Shared_Wide_Wide_String'Access);
2054         DR := Empty_Shared_Wide_Wide_String'Access;
2055
2056      --  Otherwise, allocate new shared string and fill it
2057
2058      else
2059         DL := High - Low + 1;
2060         DR := Allocate (DL);
2061         DR.Data (1 .. DL) := SR.Data (Low .. High);
2062         DR.Last := DL;
2063      end if;
2064
2065      return (AF.Controlled with Reference => DR);
2066   end Unbounded_Slice;
2067
2068   procedure Unbounded_Slice
2069     (Source : Unbounded_Wide_Wide_String;
2070      Target : out Unbounded_Wide_Wide_String;
2071      Low    : Positive;
2072      High   : Natural)
2073   is
2074      SR : constant Shared_Wide_Wide_String_Access := Source.Reference;
2075      TR : constant Shared_Wide_Wide_String_Access := Target.Reference;
2076      DL : Natural;
2077      DR : Shared_Wide_Wide_String_Access;
2078
2079   begin
2080      --  Check bounds
2081
2082      if Low > SR.Last + 1 or else High > SR.Last then
2083         raise Index_Error;
2084
2085      --  Result is empty slice, reuse empty shared string
2086
2087      elsif Low > High then
2088         Reference (Empty_Shared_Wide_Wide_String'Access);
2089         Target.Reference := Empty_Shared_Wide_Wide_String'Access;
2090         Unreference (TR);
2091
2092      else
2093         DL := High - Low + 1;
2094
2095         --  Try to reuse existent shared string
2096
2097         if Can_Be_Reused (TR, DL) then
2098            TR.Data (1 .. DL) := SR.Data (Low .. High);
2099            TR.Last := DL;
2100
2101         --  Otherwise, allocate new shared string and fill it
2102
2103         else
2104            DR := Allocate (DL);
2105            DR.Data (1 .. DL) := SR.Data (Low .. High);
2106            DR.Last := DL;
2107            Target.Reference := DR;
2108            Unreference (TR);
2109         end if;
2110      end if;
2111   end Unbounded_Slice;
2112
2113   -----------------
2114   -- Unreference --
2115   -----------------
2116
2117   procedure Unreference (Item : not null Shared_Wide_Wide_String_Access) is
2118
2119      procedure Free is
2120        new Ada.Unchecked_Deallocation
2121              (Shared_Wide_Wide_String, Shared_Wide_Wide_String_Access);
2122
2123      Aux : Shared_Wide_Wide_String_Access := Item;
2124
2125   begin
2126      if System.Atomic_Counters.Decrement (Aux.Counter) then
2127
2128         --  Reference counter of Empty_Shared_Wide_Wide_String must never
2129         --  reach zero.
2130
2131         pragma Assert (Aux /= Empty_Shared_Wide_Wide_String'Access);
2132
2133         Free (Aux);
2134      end if;
2135   end Unreference;
2136
2137end Ada.Strings.Wide_Wide_Unbounded;
2138