Skip to content

Commit

Permalink
Buffer callout results during substitution
Browse files Browse the repository at this point in the history
  • Loading branch information
ltrzesniewski committed Oct 6, 2024
1 parent 2289b6c commit 537a6ec
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 6 deletions.
90 changes: 84 additions & 6 deletions src/PCRE.NET.Native/pcrenet_substitute.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,75 @@ typedef struct
uint8_t output_on_heap;
} pcrenet_substitute_result;

typedef struct
{
int* buffer;
size_t buffer_size;
size_t count;
size_t replayed;
} replay_queue;

typedef struct
{
const pcrenet_substitute_input* input;
replay_queue callout_queue;
} substitute_callout_data;

static void replay_queue_init(replay_queue* queue)
{
queue->buffer = NULL;
queue->buffer_size = 0;
queue->count = 0;
queue->replayed = 0;
}

static void replay_queue_free(replay_queue* queue)
{
if (queue->buffer)
free(queue->buffer);

replay_queue_init(queue);
}

static void replay_queue_start_replay(replay_queue* queue)
{
queue->replayed = 0;
}

static int replay_queue_try_dequeue(replay_queue* queue, int* result)
{
if (!queue->buffer && queue->buffer_size)
return 0; // Invalid queue

if (queue->replayed < queue->count)
{
*result = queue->buffer[queue->replayed++];
return 1;
}

return 0;
}

static void replay_queue_try_enqueue(replay_queue* queue, int result)
{
if (!queue->buffer && queue->buffer_size)
return; // Invalid queue

if (queue->count == queue->buffer_size)
{
const size_t new_size = max(8, 2 * queue->buffer_size);
int* new_buf = realloc(queue->buffer, new_size * sizeof(int));
if (!new_buf)
return;

queue->buffer = new_buf;
queue->buffer_size = new_size;
}

queue->buffer[queue->count++] = result;
queue->replayed = queue->count;
}

static void free_result_memory(pcrenet_substitute_result* result)
{
if (!result)
Expand All @@ -46,8 +110,16 @@ static void free_result_memory(pcrenet_substitute_result* result)

static int substitute_callout_handler(pcre2_substitute_callout_block* block, void* data_ptr)
{
const substitute_callout_data* data = (substitute_callout_data*)data_ptr;
return data->input->callout(block, data->input->callout_data);
substitute_callout_data* data = data_ptr;

int result;
if (replay_queue_try_dequeue(&data->callout_queue, &result))
return result;

result = data->input->callout(block, data->input->callout_data);

replay_queue_try_enqueue(&data->callout_queue, result);
return result;
}

static int call_substitute(const pcrenet_substitute_input* input,
Expand Down Expand Up @@ -151,10 +223,14 @@ static void substitute_with_callout(const pcrenet_substitute_input* input,
.input = input
};

replay_queue_init(&callout_data.callout_queue);

pcre2_set_substitute_callout(match_context, &substitute_callout_handler, &callout_data);

while (1)
{
replay_queue_start_replay(&callout_data.callout_queue);

result->result_code = call_substitute(
input,
0,
Expand All @@ -168,7 +244,7 @@ static void substitute_with_callout(const pcrenet_substitute_input* input,
if (result->result_code >= 0)
{
result->output_length = output_length;
return;
break;
}

// Output buffer is too small
Expand All @@ -181,7 +257,7 @@ static void substitute_with_callout(const pcrenet_substitute_input* input,
result->output = malloc(buffer_length * sizeof(PCRE2_UCHAR));

if (!result->output)
return;
break;

result->output_on_heap = 1;
}
Expand All @@ -192,7 +268,7 @@ static void substitute_with_callout(const pcrenet_substitute_input* input,
if (!new_buffer)
{
free_result_memory(result);
return;
break;
}

result->output = new_buffer;
Expand All @@ -204,8 +280,10 @@ static void substitute_with_callout(const pcrenet_substitute_input* input,

// Error
free_result_memory(result);
return;
break;
}

replay_queue_free(&callout_data.callout_queue);
}

PCRENET_EXPORT(void, substitute)(const pcrenet_substitute_input* input, pcrenet_substitute_result* result)
Expand Down
20 changes: 20 additions & 0 deletions src/PCRE.NET.Tests/PcreNet/SubstituteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,26 @@ public void should_throw_when_callout_throws()
Assert.That(ex.InnerException, Is.InstanceOf<DivideByZeroException>());
}

[Test]
public void should_execute_each_callout_once()
{
var str = new string('a', InternalRegex.SubstituteBufferSizeInChars * (1 + 2 + 4 + 8 + 16) + 42);
var re = new PcreRegex("a");

var execCount = 0;

var result = re.Substitute(str, "#", PcreSubstituteOptions.SubstituteGlobal, data =>
{
++execCount;
Assert.That(data.SubstitutionCount, Is.EqualTo(execCount));
Assert.That(data.Match.Index, Is.EqualTo(execCount - 1));
return execCount % 3 == 0 ? PcreSubstituteCalloutResult.Pass : PcreSubstituteCalloutResult.Fail;
});

Assert.That(execCount, Is.EqualTo(str.Length));
Assert.That(result, Is.EqualTo(str.Replace("aaa", "aa#")));
}

[Test]
public void readme_replace_example()
{
Expand Down

0 comments on commit 537a6ec

Please sign in to comment.