@@ -129,7 +129,7 @@ public override void CLIMarshalToManaged(MarshalContext ctx)
129129 encoding = Context . Options . Encoding ;
130130
131131 string param ;
132- if ( Equals ( encoding , Encoding . ASCII ) )
132+ if ( Equals ( encoding , Encoding . ASCII ) || Equals ( encoding , Encoding . UTF8 ) )
133133 param = "E_UTF8" ;
134134 else if ( Equals ( encoding , Encoding . Unicode ) ||
135135 Equals ( encoding , Encoding . BigEndianUnicode ) )
@@ -154,8 +154,9 @@ public override Type CSharpSignatureType(TypePrinterContext ctx)
154154 return new CustomType ( typePrinter . IntPtrType ) ;
155155 }
156156
157- if ( Context . Options . Encoding == Encoding . ASCII )
158- return new CustomType ( "string" ) ;
157+ if ( Context . Options . Encoding == Encoding . ASCII ||
158+ Context . Options . Encoding == Encoding . UTF8 )
159+ return new CustomType ( "[MarshalAs(UnmanagedType.LPUTF8Str)] string" ) ;
159160
160161 if ( Context . Options . Encoding == Encoding . Unicode ||
161162 Context . Options . Encoding == Encoding . BigEndianUnicode )
@@ -183,19 +184,14 @@ public override void CSharpMarshalToNative(CSharpMarshalContext ctx)
183184 if ( substitution != null )
184185 param = $ "({ substitution . Replacement } ) (object) { param } ";
185186
186- if ( Equals ( Context . Options . Encoding , Encoding . ASCII ) )
187- {
188- ctx . Return . Write ( $ "Marshal.StringToHGlobalAnsi({ param } )") ;
189- return ;
190- }
191- if ( Equals ( Context . Options . Encoding , Encoding . Unicode ) ||
192- Equals ( Context . Options . Encoding , Encoding . BigEndianUnicode ) )
193- {
194- ctx . Return . Write ( $ "Marshal.StringToHGlobalUni({ param } )") ;
195- return ;
196- }
197- throw new System . NotSupportedException (
198- $ "{ Context . Options . Encoding . EncodingName } is not supported yet.") ;
187+ string bytes = $ "__bytes{ ctx . ParameterIndex } ";
188+ string bytePtr = $ "__bytePtr{ ctx . ParameterIndex } ";
189+ ctx . Before . WriteLine ( $@ "byte[] { bytes } = global::System.Text.Encoding.{
190+ GetEncodingClass ( ctx . Parameter ) } .GetBytes({ param } );" ) ;
191+ ctx . Before . WriteLine ( $ "fixed (byte* { bytePtr } = { bytes } )") ;
192+ ctx . HasCodeBlock = true ;
193+ ctx . Before . WriteOpenBraceAndIndent ( ) ;
194+ ctx . Return . Write ( $ "new global::System.IntPtr({ bytePtr } )") ;
199195 }
200196
201197 public override void CSharpMarshalToManaged ( CSharpMarshalContext ctx )
@@ -207,49 +203,93 @@ public override void CSharpMarshalToManaged(CSharpMarshalContext ctx)
207203 return ;
208204 }
209205
210- Type type = Type . Desugar ( ) ;
211- Type pointee = type . GetPointee ( ) . Desugar ( ) ;
212- var isChar = type . IsPointerToPrimitiveType ( PrimitiveType . Char ) ||
213- ( pointee . IsPointerToPrimitiveType ( PrimitiveType . Char ) &&
214- ctx . Parameter != null &&
215- ( ctx . Parameter . IsInOut || ctx . Parameter . IsOut ) ) ;
216- var encoding = isChar ? Encoding . ASCII : Encoding . Unicode ;
217-
218- if ( Equals ( encoding , Encoding . ASCII ) )
219- encoding = Context . Options . Encoding ;
220-
221206 string returnVarName = ctx . ReturnVarName ;
207+ string nullPtr = "global::System.IntPtr.Zero" ;
222208 if ( ctx . Function != null )
223209 {
224210 Type returnType = ctx . Function . ReturnType . Type . Desugar ( ) ;
225211 if ( returnType . IsAddress ( ) &&
226212 returnType . GetPointee ( ) . Desugar ( ) . IsAddress ( ) )
227213 {
228- returnVarName = $ "new global::System.IntPtr(*{ returnVarName } )";
214+ returnVarName = $ "*{ returnVarName } ";
215+ nullPtr = "null" ;
229216 }
230217 }
231218
232- if ( Equals ( encoding , Encoding . ASCII ) )
219+ TextGenerator textGenerator ;
220+ if ( ctx . Parameter == null )
233221 {
234- ctx . Return . Write ( $ "Marshal.PtrToStringAnsi({ returnVarName } )") ;
235- return ;
222+ textGenerator = ctx . Before ;
223+ textGenerator . WriteLine ( $ "if ({ ctx . ReturnVarName } == { nullPtr } )") ;
224+ textGenerator . WriteLineIndent ( $ "return default({ ctx . ReturnType } );") ;
236225 }
237- if ( Equals ( encoding , Encoding . UTF8 ) )
226+ else
238227 {
239- ctx . Return . Write ( $ "Marshal.PtrToStringUTF8({ returnVarName } )") ;
240- return ;
228+ textGenerator = ctx . Cleanup ;
229+ textGenerator . WriteLine ( $ "if ({ ctx . ReturnVarName } == { nullPtr } )") ;
230+ textGenerator . WriteOpenBraceAndIndent ( ) ;
231+ textGenerator . WriteLine ( $ "{ ctx . Parameter . Name } = default({ Type . Desugar ( ) } );") ;
232+ textGenerator . WriteLine ( "return;" ) ;
233+ textGenerator . UnindentAndWriteCloseBrace ( ) ;
241234 }
242235
243- // If we reach this, we know the string is Unicode.
244- if ( isChar || ctx . Context . TargetInfo . WCharWidth == 16 )
236+ string encoding = GetEncodingClass ( ctx . Parameter ) ;
237+ string type = GetTypeForCodePoint ( encoding ) ;
238+ textGenerator . WriteLine ( $ "var __retPtr = ({ type } *) { returnVarName } ;") ;
239+ textGenerator . WriteLine ( "int __length = 0;" ) ;
240+ textGenerator . WriteLine ( $ "while (*(__retPtr++) != 0) __length += sizeof({ type } );") ;
241+
242+ ctx . Return . Write ( $@ "global::System.Text.Encoding.{
243+ encoding } .GetString((byte*) { returnVarName } , __length)" ) ;
244+ }
245+
246+ private string GetEncodingClass ( Parameter parameter )
247+ {
248+ Type type = Type . Desugar ( ) ;
249+ Type pointee = type . GetPointee ( ) . Desugar ( ) ;
250+ var isChar = type . IsPointerToPrimitiveType ( PrimitiveType . Char ) ||
251+ ( pointee . IsPointerToPrimitiveType ( PrimitiveType . Char ) &&
252+ parameter != null &&
253+ ( parameter . IsInOut || parameter . IsOut ) ) ;
254+
255+ if ( ! isChar )
256+ return ( Context . TargetInfo . WCharWidth == 16 ) ?
257+ nameof ( Encoding . Unicode ) : nameof ( Encoding . UTF32 ) ;
258+
259+ if ( Context . Options . Encoding == Encoding . ASCII )
260+ return nameof ( Encoding . ASCII ) ;
261+
262+ if ( Context . Options . Encoding == Encoding . BigEndianUnicode )
263+ return nameof ( Encoding . BigEndianUnicode ) ;
264+
265+ if ( Context . Options . Encoding == Encoding . Unicode )
266+ return nameof ( Encoding . Unicode ) ;
267+
268+ if ( Context . Options . Encoding == Encoding . UTF32 )
269+ return nameof ( Encoding . UTF32 ) ;
270+
271+ if ( Context . Options . Encoding == Encoding . UTF7 )
272+ return nameof ( Encoding . UTF7 ) ;
273+
274+ if ( Context . Options . Encoding == Encoding . UTF8 )
275+ return nameof ( Encoding . UTF8 ) ;
276+
277+ throw new System . NotSupportedException (
278+ $ "{ Context . Options . Encoding . EncodingName } is not supported yet.") ;
279+ }
280+
281+ private static string GetTypeForCodePoint ( string encoding )
282+ {
283+ switch ( encoding )
245284 {
246- ctx . Return . Write ( $ "Marshal.PtrToStringUni({ returnVarName } )") ;
247- return ;
285+ case nameof ( Encoding . UTF32 ) :
286+ return "int" ;
287+ case nameof ( Encoding . Unicode ) :
288+ case nameof ( Encoding . BigEndianUnicode ) :
289+ return "short" ;
290+ default :
291+ return "byte" ;
248292 }
249- // If we reach this, we should have an UTF-32 wide string.
250- const string encodingName = "System.Text.Encoding.UTF32" ;
251- ctx . Return . Write ( $@ "CppSharp.Runtime.Helpers.MarshalEncodedString({
252- returnVarName } , { encodingName } )" ) ;
253293 }
254294 }
255295
0 commit comments