Skip to content

Commit 8ff7c8e

Browse files
committed
Added an API for handling strings from a char ndarray
- Added Shape.IsContagious - Minor perf-ops
1 parent edbc89c commit 8ff7c8e

File tree

4 files changed

+200
-7
lines changed

4 files changed

+200
-7
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Linq;
4+
using System.Runtime.CompilerServices;
5+
using System.Threading.Tasks;
6+
using NumSharp.Backends;
7+
using NumSharp.Backends.Unmanaged;
8+
9+
namespace NumSharp
10+
{
11+
public partial class NDArray
12+
{
13+
/// <summary>
14+
/// Converts a string to a vector ndarray of bytes.
15+
/// </summary>
16+
/// <param name="str"></param>
17+
/// <returns></returns>
18+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
19+
public static NDArray FromString(string str) => np.array(str);
20+
21+
/// <summary>
22+
/// Converts the entire <see cref="NDArray"/> to a string.
23+
/// </summary>
24+
/// <remarks>Performs a copy due to String .net-framework limitations.</remarks>
25+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
26+
public static string AsString(NDArray arr)
27+
{
28+
unsafe
29+
{
30+
Debug.Assert(arr.typecode == NPTypeCode.Char);
31+
return new string((char*)arr.Address, 0, arr.size);
32+
}
33+
}
34+
35+
/// <summary>
36+
/// Get a string out of a vector of chars.
37+
/// </summary>
38+
/// <param name="indices"></param>
39+
/// <returns></returns>
40+
/// <remarks>Performs a copy due to String .net-framework limitations.</remarks>
41+
public string GetString(params int[] indices)
42+
{
43+
unsafe
44+
{
45+
if (Shape.dimensions.Length - 1 != indices.Length)
46+
throw new ArgumentOutOfRangeException(nameof(indices), "GetString(int[]) can only accept coordinates that point to a vector of chars.");
47+
48+
Debug.Assert(typecode == NPTypeCode.Char);
49+
50+
UnmanagedStorage arr = Storage.GetData(indices);
51+
Debug.Assert(arr.Shape.NDim == 1);
52+
53+
if (!Shape.IsContagious)
54+
{
55+
//this works faster than cloning.
56+
var ret = new string('\0', arr.Count);
57+
fixed (char* retChars = ret)
58+
{
59+
var dst = new UnmanagedStorage(new ArraySlice<char>(new UnmanagedMemoryBlock<char>(retChars, ret.Length)), arr.Shape.Clean());
60+
MultiIterator.Assign(dst, arr);
61+
}
62+
63+
return ret;
64+
}
65+
66+
//new string always performs a copy, there is no need to keep reference to arr's unmanaged storage.
67+
return new string((char*)arr.Address, 0, arr.Count);
68+
}
69+
}
70+
71+
public void SetString(string value, params int[] indices)
72+
{
73+
Debug.Assert(typecode == NPTypeCode.Char);
74+
75+
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
76+
if (value == null || value.Length == 0)
77+
throw new ArgumentException("Value cannot be null or empty.", nameof(value));
78+
79+
if (Shape.dimensions.Length - 1 != indices.Length)
80+
throw new ArgumentOutOfRangeException(nameof(indices), "SetString(string, int[] indices) can only accept coordinates that point to a vector of chars.");
81+
82+
unsafe
83+
{
84+
if (Shape.IsContagious)
85+
{
86+
var dst = (char*)Address + Shape.GetOffset(indices);
87+
fixed (char* strChars = value)
88+
{
89+
var src = strChars;
90+
Parallel.For(0, value.Length, i => *(dst + i) = *(src + i));
91+
}
92+
}
93+
else
94+
{
95+
fixed (char* strChars = value)
96+
{
97+
SetData(new ArraySlice<char>(new UnmanagedMemoryBlock<char>(strChars, value.Length)), indices);
98+
}
99+
}
100+
}
101+
}
102+
103+
/// <summary>
104+
/// Get a string out of a vector of chars.
105+
/// </summary>
106+
/// <remarks>Performs a copy due to String .net-framework limitations.</remarks>
107+
public string GetStringAt(int offset)
108+
{
109+
Debug.Assert(typecode == NPTypeCode.Char);
110+
Debug.Assert(offset < Array.Count);
111+
112+
return GetString(Shape.GetCoordinates(offset));
113+
}
114+
115+
public void SetStringAt(string value, int offset)
116+
{
117+
Debug.Assert(typecode == NPTypeCode.Char);
118+
Debug.Assert(offset < Array.Count);
119+
120+
SetString(value, Shape.GetCoordinates(offset));
121+
}
122+
}
123+
}

src/NumSharp.Core/Creation/np.array.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using System.Runtime.CompilerServices;
4+
using NumSharp.Backends;
45
using NumSharp.Backends.Unmanaged;
56
using NumSharp.Utilities;
67

@@ -9,10 +10,7 @@ namespace NumSharp
910
public static partial class np
1011
{
1112
[MethodImpl((MethodImplOptions)768)]
12-
public static NDArray array(NDArray nd, bool copy = false)
13-
{
14-
return copy ? new NDArray(nd.Storage.Clone()) : new NDArray(nd.Storage);
15-
}
13+
public static NDArray array(NDArray nd, bool copy = false) => copy ? new NDArray(nd.Storage.Clone()) : new NDArray(nd.Storage);
1614

1715
public static NDArray array(Array array, Type dtype = null, int ndmin = 1, bool copy = true, char order = 'C')
1816
{
@@ -49,15 +47,27 @@ public static NDArray array(Array array, Type dtype = null, int ndmin = 1, bool
4947
}
5048

5149

52-
public static NDArray array<T>(params T[] data) where T : unmanaged
50+
public static NDArray array<T>(params T[] data) where T : unmanaged => new NDArray(ArraySlice.FromArray(data), Shape.Vector(data.Length));
51+
52+
/// <summary>
53+
/// Create a vector ndarray of type <see cref="char"/>.
54+
/// </summary>
55+
/// <param name="chars"></param>
56+
/// <returns></returns>
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public static NDArray array(string chars)
5359
{
54-
return new NDArray(ArraySlice.FromArray(data), new Shape(data.Length));
60+
if (chars == null)
61+
throw new ArgumentNullException(nameof(chars));
62+
if (chars.Length==0)
63+
return new NDArray(NPTypeCode.Char);
64+
return new NDArray(ArraySlice.FromArray(chars.ToArray()), Shape.Vector(chars.Length));
5565
}
5666

5767
public static NDArray array<T>(T[][] data)
5868
{
5969
var array = data.SelectMany(inner => inner).ToArray(); //todo! not use selectmany.
60-
return new NDArray(array, new Shape(data.Length, data[0].Length));
70+
return new NDArray(array, Shape.Matrix(data.Length, data[0].Length));
6171
}
6272

6373
public static NDArray array<T>(T[][][] data)

src/NumSharp.Core/View/Shape.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ public bool IsSliced
2525
get => ViewInfo != null;
2626
}
2727

28+
/// <summary>
29+
/// Does this Shape represents a non-sliced and non-broadcasted hence contagious unmanaged memory?
30+
/// </summary>
31+
public bool IsContagious
32+
{
33+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
34+
get => !IsSliced && !IsBroadcasted;
35+
}
36+
2837
/// <summary>
2938
/// Is this Shape a recusive view? (deeper than 1 view)
3039
/// </summary>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Linq;
3+
using FluentAssertions;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using NumSharp.UnitTest.Utilities;
6+
7+
namespace NumSharp.UnitTest.Backends.Unmanaged
8+
{
9+
[TestClass]
10+
public class StringApiTests
11+
{
12+
private static string hello = "hello";
13+
14+
[TestMethod]
15+
public void FromString()
16+
{
17+
var str = NDArray.FromString(hello);
18+
str.Should().BeOfType<char>().And.BeShaped(5).And.BeOfValues('h', 'e', 'l', 'l', 'o');
19+
}
20+
21+
[TestMethod]
22+
public void AsString()
23+
{
24+
var str = np.array('h', 'e', 'l', 'l', 'o');
25+
str.Should().BeOfType<char>().And.BeShaped(5).And.BeOfValues('h', 'e', 'l', 'l', 'o');
26+
NDArray.AsString(str).Should().Be(hello);
27+
}
28+
29+
[TestMethod]
30+
public void GetString()
31+
{
32+
var str = np.repeat(np.array('h', 'e', 'l', 'l', 'o'), 5);
33+
str.Should().BeOfType<char>().And.BeShaped(5 * 5);
34+
str = str.reshape(5, 5);
35+
str.GetString(3).Should().Be("lllll");
36+
new Action(() => { var _ = str.GetString(3, 1); }).Should().Throw<ArgumentOutOfRangeException>();
37+
}
38+
39+
[TestMethod]
40+
public void SetString()
41+
{
42+
var str = np.repeat(np.array('h', 'e', 'l', 'l', 'o'), 5);
43+
str.Should().BeOfType<char>().And.BeShaped(5 * 5);
44+
str = str.reshape(5, 5);
45+
46+
str.SetData(hello, 3);
47+
str.GetString(3).Should().Be(hello);
48+
new Action(() => { str.SetString(hello, 3, 1); }).Should().Throw<ArgumentOutOfRangeException>();
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)