Skip to content

Commit a16aa9d

Browse files
committed
[[ Chunks ]] Optimize delimited chunk evaluation.
An optimized operation MCStringForwardDelimitedRegion has been added which implements the operation of getting a single chunk or chunk range from a string when the chunk is of delimited type. This commit uses the operation for the 'item' chunk where both start and finish indicies are non-negative.
1 parent 3c2e0da commit a16aa9d

File tree

6 files changed

+279
-3
lines changed

6 files changed

+279
-3
lines changed

engine/src/chunk.cpp

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,167 @@ static void skip_word(const char *&sptr, const char *&eptr)
22832283
}
22842284
#endif
22852285

2286+
void MCStringsMarkTextChunkByOrdinal(MCExecContext& ctxt, Chunk_term p_chunk_type, Chunk_term p_ordinal_type, bool p_force, bool p_whole_chunk, bool p_further_chunks, MCMarkedText& x_mark);
2287+
void MCStringsMarkTextChunkByRange(MCExecContext& ctxt, Chunk_term p_chunk_type, integer_t p_first, integer_t p_last, bool p_force, bool p_whole_chunk, bool p_further_chunks, MCMarkedText& x_mark);
2288+
2289+
template
2290+
<
2291+
Chunk_term ChunkType,
2292+
void (*MarkRange)(MCExecContext& ctxt, int4 first, int4 last, MCMarkedText& x_mark)
2293+
>
2294+
static inline bool __MCCRefMarkForEval(MCExecContext& ctxt, MCCRef *self, MCMarkedText& x_mark)
2295+
{
2296+
int4 t_first, t_last;
2297+
if (self -> etype == CT_RANGE || self -> etype == CT_EXPRESSION)
2298+
{
2299+
if (!ctxt . EvalExprAsInt(self -> startpos, EE_CHUNK_BADRANGESTART, t_first))
2300+
return false;
2301+
2302+
if (self -> etype != CT_RANGE)
2303+
{
2304+
t_last = t_first;
2305+
}
2306+
else
2307+
{
2308+
if (!ctxt . EvalExprAsInt(self -> endpos, EE_CHUNK_BADRANGEEND, t_last))
2309+
return false;
2310+
}
2311+
}
2312+
else
2313+
{
2314+
switch(self -> etype)
2315+
{
2316+
case CT_ANY:
2317+
case CT_MIDDLE:
2318+
if (ChunkType != CT_BYTE)
2319+
MCStringsMarkTextChunkByOrdinal(ctxt,
2320+
ChunkType,
2321+
self -> etype,
2322+
false,
2323+
false,
2324+
false,
2325+
x_mark);
2326+
else
2327+
MCStringsMarkBytesOfTextByOrdinal(ctxt,
2328+
self -> etype,
2329+
x_mark);
2330+
return true;
2331+
case CT_LAST:
2332+
t_first = -1;
2333+
t_last = -1;
2334+
break;
2335+
case CT_FIRST:
2336+
case CT_SECOND:
2337+
case CT_THIRD:
2338+
case CT_FOURTH:
2339+
case CT_FIFTH:
2340+
case CT_SIXTH:
2341+
case CT_SEVENTH:
2342+
case CT_EIGHTH:
2343+
case CT_NINTH:
2344+
case CT_TENTH:
2345+
t_first = self -> etype - CT_FIRST;
2346+
t_last = t_first;
2347+
break;
2348+
default:
2349+
ctxt . LegacyThrow(EE_CHUNK_BADEXTENTS);
2350+
return false;
2351+
}
2352+
}
2353+
2354+
MarkRange(ctxt, t_first, t_last, x_mark);
2355+
2356+
return true;
2357+
}
2358+
2359+
template
2360+
<
2361+
Chunk_term ChunkType
2362+
>
2363+
static inline void __MCCRefMarkChunkRangeForEval(MCExecContext& ctxt, int4 p_first, int4 p_last, MCMarkedText& x_mark)
2364+
{
2365+
if (ChunkType != CT_BYTE)
2366+
MCStringsMarkTextChunkByRange(ctxt, ChunkType, p_first, p_last, false, false, false, x_mark);
2367+
else
2368+
MCStringsMarkBytesOfTextByRange(ctxt, p_first, p_last, x_mark);
2369+
}
2370+
2371+
static inline void __MCCRefMarkItemRangeForEval(MCExecContext& ctxt, int4 p_first, int4 p_last, MCMarkedText& x_mark)
2372+
{
2373+
if (p_first < 0 || p_last < 0)
2374+
{
2375+
MCStringsMarkTextChunkByRange(ctxt, CT_ITEM, p_first, p_last, false, false, false, x_mark);
2376+
return;
2377+
}
2378+
2379+
if (p_first > p_last)
2380+
p_last = p_first - 1;
2381+
else if (p_first == 0)
2382+
p_first = 1;
2383+
2384+
MCRange t_range;
2385+
MCStringForwardDelimitedRegion((MCStringRef)x_mark . text,
2386+
MCRangeMakeMinMax(x_mark . start, x_mark . finish),
2387+
ctxt . GetItemDelimiter(),
2388+
(uindex_t)(p_first - 1),
2389+
(uindex_t)p_last,
2390+
ctxt . GetStringComparisonType(),
2391+
t_range);
2392+
2393+
x_mark . start = t_range . offset;
2394+
x_mark . finish = t_range . offset + t_range . length;
2395+
}
2396+
2397+
#define __MCCRefMarkChunkForEval(chunk, ctxt, cref, x_mark) \
2398+
__MCCRefMarkForEval< chunk, __MCCRefMarkChunkRangeForEval<chunk> >(ctxt, cref, x_mark)
2399+
2400+
void MCChunk::mark_for_eval(MCExecContext& ctxt, MCMarkedText& x_mark)
2401+
{
2402+
if (cline != nil &&
2403+
__MCCRefMarkChunkForEval(CT_LINE, ctxt, cline, x_mark))
2404+
return;
2405+
2406+
if (paragraph != nil &&
2407+
__MCCRefMarkChunkForEval(CT_PARAGRAPH, ctxt, paragraph, x_mark))
2408+
return;
2409+
2410+
if (sentence != nil &&
2411+
__MCCRefMarkChunkForEval(CT_SENTENCE, ctxt, sentence, x_mark))
2412+
return;
2413+
2414+
if (item != nil &&
2415+
__MCCRefMarkForEval< CT_ITEM, __MCCRefMarkItemRangeForEval>(ctxt, item, x_mark))
2416+
return;
2417+
2418+
if (word != nil &&
2419+
__MCCRefMarkChunkForEval(CT_WORD, ctxt, word, x_mark))
2420+
return;
2421+
2422+
if (trueword != nil &&
2423+
__MCCRefMarkChunkForEval(CT_TRUEWORD, ctxt, trueword, x_mark))
2424+
return;
2425+
2426+
if (token != nil &&
2427+
__MCCRefMarkChunkForEval(CT_TOKEN, ctxt, token, x_mark))
2428+
return;
2429+
2430+
if (character != nil &&
2431+
__MCCRefMarkChunkForEval(CT_CHARACTER, ctxt, character, x_mark))
2432+
return;
2433+
2434+
if (codepoint != nil &&
2435+
__MCCRefMarkChunkForEval(CT_CODEPOINT, ctxt, codepoint, x_mark))
2436+
return;
2437+
2438+
if (codeunit != nil &&
2439+
__MCCRefMarkChunkForEval(CT_CODEUNIT, ctxt, codeunit, x_mark))
2440+
return;
2441+
2442+
if (byte != nil &&
2443+
!__MCCRefMarkChunkForEval(CT_BYTE, ctxt, byte, x_mark))
2444+
return;
2445+
}
2446+
22862447
void MCChunk::mark(MCExecContext &ctxt, bool force, bool wholechunk, MCMarkedText& x_mark, bool includechars)
22872448
{
22882449
int4 t_first, t_last;
@@ -3116,16 +3277,22 @@ void MCChunk::eval_ctxt(MCExecContext &ctxt, MCExecValue &r_text)
31163277
t_new_mark . finish = MCDataGetLength(*t_data);
31173278
}
31183279

3119-
mark(ctxt, false, false, t_new_mark);
3280+
mark_for_eval(ctxt, t_new_mark);
31203281

31213282
// SN-2014-12-15: [[ Bug 14211 ]] mark() can throw errors
31223283
if (ctxt . HasError())
31233284
return;
31243285

31253286
if (!isdatachunk())
3126-
MCStringsEvalTextChunk(ctxt, t_new_mark, r_text . stringref_value), r_text . type = kMCExecValueTypeStringRef;
3287+
{
3288+
MCStringsEvalTextChunk(ctxt, t_new_mark, r_text . stringref_value);
3289+
r_text . type = kMCExecValueTypeStringRef;
3290+
}
31273291
else
3128-
MCStringsEvalByteChunk(ctxt, t_new_mark, r_text . dataref_value), r_text . type = kMCExecValueTypeDataRef;
3292+
{
3293+
MCStringsEvalByteChunk(ctxt, t_new_mark, r_text . dataref_value);
3294+
r_text . type = kMCExecValueTypeDataRef;
3295+
}
31293296

31303297
MCValueRelease(t_new_mark . text);
31313298
}

engine/src/chunk.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class MCChunk : public MCExpression
130130
/* WRAPPER */ Exec_stat mark(MCExecPoint &ep, Boolean force, Boolean wholechunk, MCMarkedText& r_mark, bool includechars = true);
131131
#endif
132132
void mark(MCExecContext &ctxt, bool set, bool wholechunk, MCMarkedText& x_mark, bool includechars = true);
133+
void mark_for_eval(MCExecContext& ctxt, MCMarkedText& x_mark);
133134
#ifdef LEGACY_EXEC
134135
Exec_stat mark_legacy(MCExecPoint &, int4 &start, int4 &end, Boolean force, Boolean wholechunk, bool include_characters = true);
135136

libfoundation/include/foundation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,8 @@ MC_DLLEXPORT bool MCStringBreakIntoChunks(MCStringRef string, codepoint_t separa
23292329
// Note: If needle is the empty string then false will be returned.
23302330
MC_DLLEXPORT bool MCStringDelimitedOffset(MCStringRef string, MCRange range, MCStringRef needle, MCStringRef delimiter, uindex_t skip, MCStringOptions options, uindex_t& r_index, MCRange *r_found, MCRange *r_before, MCRange *r_after);
23312331

2332+
MC_DLLEXPORT bool MCStringForwardDelimitedRegion(MCStringRef string, MCRange range, MCStringRef delimiter, uindex_t first, uindex_t last, MCStringOptions options, MCRange& r_range);
2333+
23322334
//////////
23332335

23342336
// Transform the string to its folded form as specified by 'options'. The folded

libfoundation/src/foundation-string.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,6 +3654,69 @@ bool MCStringDelimitedOffset(MCStringRef self, MCRange p_range, MCStringRef p_ne
36543654
r_after);
36553655
}
36563656

3657+
MC_DLLEXPORT bool MCStringForwardDelimitedRegion(MCStringRef self,
3658+
MCRange p_range,
3659+
MCStringRef p_delimiter,
3660+
uindex_t p_first,
3661+
uindex_t p_last,
3662+
MCStringOptions p_options,
3663+
MCRange& r_range)
3664+
{
3665+
__MCAssertIsString(self);
3666+
__MCAssertIsString(p_delimiter);
3667+
3668+
if (__MCStringIsIndirect(self))
3669+
self = self -> string;
3670+
3671+
if (__MCStringIsIndirect(p_delimiter))
3672+
p_delimiter = p_delimiter -> string;
3673+
3674+
__MCStringClampRange(self, p_range);
3675+
3676+
uindex_t t_start, t_finish;
3677+
t_start = p_range . offset;
3678+
t_finish = p_range . offset + p_range . length;
3679+
3680+
MCRange t_first_del;
3681+
t_first_del = MCRangeMake(0, 0);
3682+
if (p_first > 0 &&
3683+
!__MCStringSkip(self,
3684+
MCRangeMakeMinMax(t_start, t_finish),
3685+
p_delimiter,
3686+
p_first,
3687+
p_options,
3688+
t_first_del))
3689+
{
3690+
r_range = MCRangeMake(t_finish,
3691+
0);
3692+
return true;
3693+
}
3694+
3695+
t_start = t_first_del . offset + t_first_del . length;
3696+
3697+
if (p_first >= p_last)
3698+
{
3699+
r_range = MCRangeMakeMinMax(t_start, 0);
3700+
return true;
3701+
}
3702+
3703+
MCRange t_last_del;
3704+
if (!__MCStringSkip(self,
3705+
MCRangeMake(t_start, t_finish),
3706+
p_delimiter,
3707+
p_last - p_first,
3708+
p_options,
3709+
t_last_del))
3710+
{
3711+
r_range = MCRangeMakeMinMax(t_start, t_finish);
3712+
return true;
3713+
}
3714+
3715+
r_range = MCRangeMakeMinMax(t_start, t_last_del . offset);
3716+
3717+
return true;
3718+
}
3719+
36573720
////////////////////////////////////////////////////////////////////////////////
36583721

36593722
MC_DLLEXPORT_DEF
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
script "CoreStringsDelimitedChunk"
2+
/*
3+
Copyright (C) 2015 LiveCode Ltd.
4+
5+
This file is part of LiveCode.
6+
7+
LiveCode is free software; you can redistribute it and/or modify it under
8+
the terms of the GNU General Public License v3 as published by the Free
9+
Software Foundation.
10+
11+
LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY
12+
WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
18+
19+
on TestForwardRanges
20+
TestAssert "uncased delimiter front", item 1 of "foo,foobar,bar,barbaz,baz" is "foo"
21+
TestAssert "uncased delimiter middle", item 3 of "foo,foobar,bar,barbaz,baz" is "bar"
22+
TestAssert "uncased delimiter back", item 5 of "foo,foobar,bar,barbaz,baz" is "baz"
23+
TestAssert "uncased delimiter before", item 0 of "foo,foobar,bar,barbaz,baz" is empty
24+
TestAssert "uncased delimiter after", item 6 of "foo,foobar,bar,barbaz,baz" is empty
25+
26+
TestAssert "uncased delimiter range front", item 1 to 2 of "foo,foobar,bar,barbaz,baz" is "foo,foobar"
27+
TestAssert "uncased delimiter range middle", item 2 to 4 of "foo,foobar,bar,barbaz,baz" is "foobar,bar,barbaz"
28+
TestAssert "uncased delimiter range back", item 4 to 5 of "foo,foobar,bar,barbaz,baz" is "barbaz,baz"
29+
TestAssert "uncased delimiter range before", item 0 to 2 of "foo,foobar,bar,barbaz,baz" is "foo,foobar"
30+
TestAssert "uncased delimiter range after", item 4 to 6 of "foo,foobar,bar,barbaz,baz" is "barbaz,baz"
31+
32+
TestAssert "uncased trailing delimiter front", item 1 of "foo,foobar,bar,barbaz,baz," is "foo"
33+
TestAssert "uncased trailing delimiter middle", item 3 of "foo,foobar,bar,barbaz,baz," is "bar"
34+
TestAssert "uncased trailing delimiter back", item 5 of "foo,foobar,bar,barbaz,baz," is "baz"
35+
TestAssert "uncased trailing delimiter before", item 0 of "foo,foobar,bar,barbaz,baz," is empty
36+
TestAssert "uncased trailing delimiter after", item 6 of "foo,foobar,bar,barbaz,baz," is empty
37+
38+
TestAssert "uncased trailing delimiter range front", item 1 to 2 of "foo,foobar,bar,barbaz,baz," is "foo,foobar"
39+
TestAssert "uncased trailing delimiter range middle", item 2 to 4 of "foo,foobar,bar,barbaz,baz," is "foobar,bar,barbaz"
40+
TestAssert "uncased trailing delimiter range back", item 4 to 5 of "foo,foobar,bar,barbaz,baz," is "barbaz,baz"
41+
TestAssert "uncased trailing delimiter range before", item 0 to 2 of "foo,foobar,bar,barbaz,baz," is "foo,foobar"
42+
TestAssert "uncased trailing delimiter range after", item 4 to 6 of "foo,foobar,bar,barbaz,baz," is "barbaz,baz,"
43+
end TestForwardRanges

tests/lcs/core/strings/chunkoffset.livecodescript renamed to tests/lcs/core/strings/delimitedchunkoffset.livecodescript

File renamed without changes.

0 commit comments

Comments
 (0)