1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . A R R A Y _ S P L I T --
9 -- Copyright (C) 2002-2023, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada
.Unchecked_Deallocation
;
34 package body GNAT
.Array_Split
is
37 new Ada
.Unchecked_Deallocation
(Slices_Indexes
, Slices_Access
);
40 new Ada
.Unchecked_Deallocation
(Separators_Indexes
, Indexes_Access
);
43 (Source
: Element_Sequence
;
44 Pattern
: Element_Set
) return Natural;
45 -- Returns the number of occurrences of Pattern elements in Source, 0 is
46 -- returned if no occurrence is found in Source.
52 overriding
procedure Adjust
(S
: in out Slice_Set
) is
54 S
.D
.Ref_Counter
:= S
.D
.Ref_Counter
+ 1;
63 From
: Element_Sequence
;
64 Separators
: Element_Sequence
;
65 Mode
: Separator_Mode
:= Single
)
68 Create
(S
, From
, To_Set
(Separators
), Mode
);
72 (From
: Element_Sequence
;
73 Separators
: Element_Sequence
;
74 Mode
: Separator_Mode
:= Single
) return Slice_Set
is
76 return Ret
: Slice_Set
do
77 Create
(Ret
, From
, Separators
, Mode
);
87 From
: Element_Sequence
;
88 Separators
: Element_Set
;
89 Mode
: Separator_Mode
:= Single
)
93 Result
.D
.Source
:= new Element_Sequence
'(From);
94 Set (Result, Separators, Mode);
99 (From : Element_Sequence;
100 Separators : Element_Set;
101 Mode : Separator_Mode := Single) return Slice_Set is
103 return Ret : Slice_Set do
104 Create (Ret, From, Separators, Mode);
113 (Source : Element_Sequence;
114 Pattern : Element_Set) return Natural
118 for K in Source'Range loop
119 if Is_In (Source (K), Pattern) then
131 overriding procedure Finalize (S : in out Slice_Set) is
134 new Ada.Unchecked_Deallocation (Element_Sequence, Element_Access);
137 new Ada.Unchecked_Deallocation (Data, Data_Access);
139 D : Data_Access := S.D;
142 -- Ensure call is idempotent
147 D.Ref_Counter := D.Ref_Counter - 1;
149 if D.Ref_Counter = 0 then
162 overriding procedure Initialize (S : in out Slice_Set) is
164 S.D := new Data'(1, null, 0, null, null);
173 Index
: Slice_Number
) return Slice_Separators
176 if Index
> S
.D
.N_Slice
then
180 or else (Index
= 1 and then S
.D
.N_Slice
= 1)
182 -- Whole string, or no separator used
184 return [Before
=> Array_End
,
188 return [Before
=> Array_End
,
189 After
=> S
.D
.Source
(S
.D
.Slices
(Index
).Stop
+ 1)];
191 elsif Index
= S
.D
.N_Slice
then
192 return [Before
=> S
.D
.Source
(S
.D
.Slices
(Index
).Start
- 1),
196 return [Before
=> S
.D
.Source
(S
.D
.Slices
(Index
).Start
- 1),
197 After
=> S
.D
.Source
(S
.D
.Slices
(Index
).Stop
+ 1)];
205 function Separators
(S
: Slice_Set
) return Separators_Indexes
is
207 return S
.D
.Indexes
.all;
215 (S
: in out Slice_Set
;
216 Separators
: Element_Sequence
;
217 Mode
: Separator_Mode
:= Single
)
220 Set
(S
, To_Set
(Separators
), Mode
);
228 (S
: in out Slice_Set
;
229 Separators
: Element_Set
;
230 Mode
: Separator_Mode
:= Single
)
233 procedure Copy_On_Write
(S
: in out Slice_Set
);
234 -- Make a copy of S if shared with another variable
240 procedure Copy_On_Write
(S
: in out Slice_Set
) is
242 if S
.D
.Ref_Counter
> 1 then
243 -- First let's remove our count from the current data
245 S
.D
.Ref_Counter
:= S
.D
.Ref_Counter
- 1;
247 -- Then duplicate the data
249 S
.D
:= new Data
'(S.D.all);
250 S.D.Ref_Counter := 1;
252 if S.D.Source /= null then
253 S.D.Source := new Element_Sequence'(S
.D
.Source
.all);
259 -- If there is a single reference to this variable, free it now
260 -- as it will be redefined below.
267 Count_Sep
: constant Natural := Count
(S
.D
.Source
.all, Separators
);
273 -- Compute all separator's indexes
275 S
.D
.Indexes
:= new Separators_Indexes
(1 .. Count_Sep
);
276 J
:= S
.D
.Indexes
'First;
278 for K
in S
.D
.Source
'Range loop
279 if Is_In
(S
.D
.Source
(K
), Separators
) then
280 S
.D
.Indexes
(J
) := K
;
285 -- Compute slice info for fast slice access
288 S_Info
: Slices_Indexes
(1 .. Slice_Number
(Count_Sep
) + 1);
290 Start
, Stop
: Natural;
295 Start
:= S
.D
.Source
'First;
299 if K
> Count_Sep
then
301 -- No more separators, last slice ends at end of source string
303 Stop
:= S
.D
.Source
'Last;
306 Stop
:= S
.D
.Indexes
(K
) - 1;
309 -- Add slice to the table
311 S
.D
.N_Slice
:= S
.D
.N_Slice
+ 1;
312 S_Info
(S
.D
.N_Slice
) := (Start
, Stop
);
314 exit when K
> Count_Sep
;
319 -- In this mode just set start to character next to the
320 -- current separator, advance the separator index.
322 Start
:= S
.D
.Indexes
(K
) + 1;
327 -- In this mode skip separators following each other
330 Start
:= S
.D
.Indexes
(K
) + 1;
332 exit when K
> Count_Sep
333 or else S
.D
.Indexes
(K
) > S
.D
.Indexes
(K
- 1) + 1;
338 S
.D
.Slices
:= new Slices_Indexes
'(S_Info (1 .. S.D.N_Slice));
348 Index : Slice_Number) return Element_Sequence
352 return S.D.Source.all;
354 elsif Index > S.D.N_Slice then
359 S.D.Source (S.D.Slices (Index).Start .. S.D.Slices (Index).Stop);
367 function Slice_Count (S : Slice_Set) return Slice_Number is
372 end GNAT.Array_Split;