Skip to content

Commit f05c4a3

Browse files
authored
Swig CSharp: Add additional support for UTF-8 strings (#14075)
Add UTF-8 encoding/decoding support to Gdal options functions The following Gdal functions now accept UTF-8 strings for their `pszDefault` parameter and will return UTF-8 strings. - GetConfigOption - GetGlobalConfigOption - GetThreadLocalConfigOption - GetCredential - GetPathSpecificOption The following Gdal functions now accept UTF-8 strings for their `pszValue` parameter. - SetConfigOption - SetThreadLocalConfigOption - SetCredential - SetPathSpecificOption Changed the C# `utf8_path` typemap to use the built-in UTF-8 encoding marshaller.
1 parent c310fce commit f05c4a3

File tree

4 files changed

+88
-13
lines changed

4 files changed

+88
-13
lines changed

swig/csharp/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,16 @@ if (BUILD_TESTING)
701701
SYSTEM_DEPENDS
702702
OSGeo.GDAL)
703703

704+
gdal_build_csharp_sample(
705+
OUTPUT
706+
GDALTestUtf8.exe
707+
SOURCE
708+
${CMAKE_CURRENT_SOURCE_DIR}/apps/GDALTestUtf8.cs
709+
DEPENDS
710+
gdal_csharp
711+
SYSTEM_DEPENDS
712+
OSGeo.GDAL)
713+
704714
gdal_build_csharp_sample(
705715
OUTPUT
706716
OGRLayerAlg.exe
@@ -766,6 +776,7 @@ if (BUILD_TESTING)
766776
OSGeo.GDAL.Samples.GDALOverviews
767777
OSGeo.GDAL.Samples.GDALCreateCopy
768778
OSGeo.GDAL.Samples.GDALGetHistogram
779+
OSGeo.GDAL.Samples.GDALTestUtf8
769780
OSGeo.GDAL.Samples.GDALTest
770781
OSGeo.GDAL.Samples.OGRLayerAlg
771782
OSGeo.GDAL.Samples.GDALWarp
@@ -863,4 +874,9 @@ if (BUILD_TESTING)
863874
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
864875
COMMAND ${CSHARP_INTERPRETER} GetCRSInfo/GetCRSInfo${_ex} "EPSG" 20)
865876
set_property(TEST csharp_getcrsinfo PROPERTY ENVIRONMENT "${TEST_ENV}")
877+
add_test(
878+
NAME csharp_testutf8
879+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
880+
COMMAND ${CSHARP_INTERPRETER} GDALTestUtf8/GDALTestUtf8${_ex})
881+
set_property(TEST csharp_testutf8 PROPERTY ENVIRONMENT "${TEST_ENV}")
866882
endif()

swig/csharp/apps/GDALTestUtf8.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using OSGeo.GDAL;
3+
4+
namespace testapp
5+
{
6+
class GdalTestUtf8
7+
{
8+
const string ConfigKey = "UNICODE_STRING";
9+
const string UnicodeString = "Ĥĕļĺō Ŵŏŕľď";
10+
11+
public static void Main()
12+
{
13+
Console.OutputEncoding = System.Text.Encoding.UTF8;
14+
RunTest("ConfigOption", Gdal.GetConfigOption, Gdal.SetConfigOption);
15+
RunTest("ThreadLocalConfigOption", Gdal.GetThreadLocalConfigOption, Gdal.SetThreadLocalConfigOption);
16+
RunTest("ThreadLocalConfigOption", (k, d) => Gdal.GetPathSpecificOption("/", k, d), (k, v) => Gdal.SetPathSpecificOption("/", k, v));
17+
RunTest("Credential", (k, d) => Gdal.GetCredential("/", k, d), (k, v) => Gdal.SetCredential("/", k, v));
18+
}
19+
20+
private static void RunTest(string name, Func<string, string, string> getter, Action<string, string> setter)
21+
{
22+
Console.WriteLine($"Testing {name}");
23+
Console.WriteLine($" Setting and getting Unicode string");
24+
setter(ConfigKey, UnicodeString);
25+
var configValue = getter(ConfigKey, null);
26+
AssertEqual(UnicodeString, configValue, name);
27+
28+
Console.WriteLine($" Setting null value");
29+
setter(ConfigKey, null);
30+
configValue = getter(ConfigKey, null);
31+
AssertEqual(null, configValue, name);
32+
33+
Console.WriteLine($" Getting unset key with Unicode default value");
34+
configValue = getter(ConfigKey, UnicodeString);
35+
AssertEqual(UnicodeString, configValue, name);
36+
}
37+
38+
private static void AssertEqual(string expected, string actual, string funcName)
39+
{
40+
if (expected == actual)
41+
{
42+
Console.WriteLine($" {funcName} returned the expected value: '{expected ?? "NULL"}'");
43+
}
44+
else
45+
{
46+
throw new Exception($"Expected '{expected}' but {funcName} returned '{actual ?? "NULL"}'");
47+
}
48+
}
49+
}
50+
}
51+

swig/include/cpl.i

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@ void VSICloseDir(VSIDIR* dir);
470470

471471
#endif
472472

473+
/* Utf-8 string marshalling for C# */
474+
#if defined(SWIGCSHARP)
475+
%apply (const char *utf8_or_null) { (const char* pszValue), (const char* pszDefault) };
476+
#endif
473477
%apply Pointer NONNULL {const char * pszKey};
474478
void CPLSetConfigOption( const char * pszKey, const char * pszValue );
475479
void CPLSetThreadLocalConfigOption( const char * pszKey, const char * pszValue );
@@ -532,6 +536,9 @@ const char *wrapper_VSIGetPathSpecificOption( const char* pszPathPrefix, const c
532536

533537
%clear const char * pszPathPrefix;
534538
%clear const char * pszKey;
539+
#if defined(SWIGCSHARP)
540+
%clear (const char* pszValue), (const char* pszDefault);
541+
#endif
535542

536543

537544
%inline {

swig/include/csharp/typemaps_csharp.i

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -477,20 +477,21 @@ CSHARP_OBJECT_ARRAYS_PINNED(GDALRasterBandShadow, Band)
477477
%apply (int inout[ANY]) {int *pList};
478478

479479
/*
480-
* Typemap for const char *utf8_path.
480+
* Typemap for const char *utf8_path and utf8_or_null.
481481
*/
482-
%typemap(csin) (const char *utf8_path) "$module.StringToUtf8Bytes($csinput)"
483-
%typemap(imtype, out="IntPtr") (const char *utf8_path) "byte[]"
484-
%typemap(out) (const char *utf8_path) %{ $result = $1; %}
485-
%typemap(csout, excode=SWIGEXCODE) (const char *utf8_path) {
486-
/* %typemap(csout) (const char *utf8_path) */
487-
IntPtr cPtr = $imcall;
488-
string ret = $module.Utf8BytesToString(cPtr);
489-
$excode
490-
return ret;
491-
}
492-
493-
%apply ( const char *utf8_path ) { const char* GetFieldAsString };
482+
%typemap(imtype,
483+
inattributes="[MarshalAs(UnmanagedType.LPUTF8Str)]",
484+
outattributes="[return: MarshalAs(UnmanagedType.LPUTF8Str)]")
485+
(const char *utf8_path), (const char *utf8_or_null = NULL) "string"
486+
487+
%apply ( const char *utf8_path ) {
488+
const char* GetFieldAsString,
489+
const char* wrapper_CPLGetConfigOption,
490+
const char* wrapper_CPLGetGlobalConfigOption,
491+
const char* wrapper_CPLGetThreadLocalConfigOption,
492+
const char* wrapper_VSIGetCredential,
493+
const char* wrapper_VSIGetPathSpecificOption
494+
};
494495

495496
/*
496497
* Typemap for double *defaultval.

0 commit comments

Comments
 (0)