1#include "duckdb/function/scalar/string_functions.hpp"
2
3#include "duckdb/common/exception.hpp"
4#include "duckdb/common/vector_operations/vector_operations.hpp"
5#include "duckdb/common/vector_operations/unary_executor.hpp"
6#include "utf8proc.hpp"
7
8#include <string.h>
9
10using namespace std;
11
12namespace duckdb {
13
14//! Fast ASCII string reverse, returns false if the input data is not ascii
15static bool strreverse_ascii(const char *input, idx_t n, char *output) {
16 for (idx_t i = 0; i < n; i++) {
17 if (input[i] & 0x80) {
18 // non-ascii character
19 return false;
20 }
21 output[n - i - 1] = input[i];
22 }
23 return true;
24}
25
26//! Unicode string reverse using grapheme breakers
27static void strreverse_unicode(const char *input, idx_t n, char *output) {
28 utf8proc_grapheme_callback(input, n, [&](size_t start, size_t end) {
29 memcpy(output + n - end, input + start, end - start);
30 return true;
31 });
32}
33
34static void reverse_chunk_function(DataChunk &args, ExpressionState &state, Vector &result) {
35 assert(args.column_count() == 1);
36 assert(args.data[0].type == TypeId::VARCHAR);
37
38 UnaryExecutor::Execute<string_t, string_t, true>(args.data[0], result, args.size(), [&](string_t input) {
39 auto input_data = input.GetData();
40 auto input_length = input.GetSize();
41
42 auto target = StringVector::EmptyString(result, input_length);
43 auto target_data = target.GetData();
44 if (!strreverse_ascii(input_data, input_length, target_data)) {
45 strreverse_unicode(input_data, input_length, target_data);
46 }
47 target.Finalize();
48 return target;
49 });
50}
51
52void ReverseFun::RegisterFunction(BuiltinFunctions &set) {
53 set.AddFunction(ScalarFunction("reverse", {SQLType::VARCHAR}, SQLType::VARCHAR, reverse_chunk_function));
54}
55
56} // namespace duckdb
57