Skip to content

Instantly share code, notes, and snippets.

@espresso3389
Last active October 30, 2023 08:23
Show Gist options
  • Save espresso3389/2007f5050130e12525b556ef8d4b7c3f to your computer and use it in GitHub Desktop.
Save espresso3389/2007f5050130e12525b556ef8d4b7c3f to your computer and use it in GitHub Desktop.
Using icu from Dart/Flutter
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:unorm_dart/unorm_dart.dart' as unorm_dart; // fallback
String _getLibicuModuleName() {
if (Platform.isAndroid) {
return 'libicu.so';
} else if (Platform.isWindows) {
return 'icuuc.dll';
}
throw Exception('Unsupported platform');
}
final _libicu = DynamicLibrary.open(_getLibicuModuleName());
final _unorm2_getNFKCInstance = _libicu
.lookupFunction<Pointer Function(Pointer<Uint32>), Pointer Function(Pointer<Uint32>)>('unorm2_getNFKCInstance');
final _unorm2_normalize = _libicu.lookupFunction<
Int32 Function(Pointer, Pointer<Utf16>, Int32, Pointer<Utf16>, Int32, Pointer<Uint32>),
int Function(Pointer, Pointer<Utf16>, int, Pointer<Utf16>, int, Pointer<Uint32>)>('unorm2_normalize');
Pointer _getNFKCInstance() {
return using((arena) {
final err = arena.allocate<Uint32>(4);
err.value = 0;
final unorm = _unorm2_getNFKCInstance(err);
if (err.value > 0 || unorm.address == 0) {
throw Exception('unorm2_getNFKCInstance failed: ${err.value}');
}
return unorm;
});
}
bool? _haveIcu;
Pointer _unorm = Pointer.fromAddress(0);
String nfkc(String str) {
return using((arena) {
final err = arena.allocate<Uint32>(4);
try {
if (_haveIcu == null) {
_unorm = _getNFKCInstance();
_haveIcu = true;
}
} catch (e) {
_haveIcu = false;
}
if (_haveIcu == false) {
return unorm_dart.nfkc(str);
}
final src = str.toNativeUtf16(allocator: arena);
err.value = 0;
final destLength = _unorm2_normalize(_unorm, src, -1, Pointer.fromAddress(0), 0, err);
// https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/utypes_8h_source.html#l00717
const U_BUFFER_OVERFLOW_ERROR = 15;
if (err.value > 0 && err.value != U_BUFFER_OVERFLOW_ERROR) {
throw Exception('unorm2_normalize failed: ${err.value}');
}
final dest = arena.allocate<Utf16>((destLength + 1) * 2);
err.value = 0;
_unorm2_normalize(_unorm, src, -1, dest, destLength + 1, err);
if (err.value > 0 && err.value != U_BUFFER_OVERFLOW_ERROR) {
throw Exception('unorm2_normalize failed: ${err.value}');
}
return dest.toDartString();
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment