arraykit 0.6.1__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {arraykit-0.6.1/arraykit.egg-info → arraykit-0.6.2}/PKG-INFO +1 -1
  2. {arraykit-0.6.1 → arraykit-0.6.2}/README.rst +8 -0
  3. {arraykit-0.6.1 → arraykit-0.6.2/arraykit.egg-info}/PKG-INFO +1 -1
  4. {arraykit-0.6.1 → arraykit-0.6.2}/setup.py +1 -1
  5. {arraykit-0.6.1 → arraykit-0.6.2}/src/_arraykit.c +276 -268
  6. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_nonzero_1d.py +16 -1
  7. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_tri_map.py +15 -1
  8. {arraykit-0.6.1 → arraykit-0.6.2}/LICENSE.txt +0 -0
  9. {arraykit-0.6.1 → arraykit-0.6.2}/MANIFEST.in +0 -0
  10. {arraykit-0.6.1 → arraykit-0.6.2}/arraykit.egg-info/SOURCES.txt +0 -0
  11. {arraykit-0.6.1 → arraykit-0.6.2}/arraykit.egg-info/dependency_links.txt +0 -0
  12. {arraykit-0.6.1 → arraykit-0.6.2}/arraykit.egg-info/requires.txt +0 -0
  13. {arraykit-0.6.1 → arraykit-0.6.2}/arraykit.egg-info/top_level.txt +0 -0
  14. {arraykit-0.6.1 → arraykit-0.6.2}/setup.cfg +0 -0
  15. {arraykit-0.6.1 → arraykit-0.6.2}/src/__init__.py +0 -0
  16. {arraykit-0.6.1 → arraykit-0.6.2}/src/__init__.pyi +0 -0
  17. {arraykit-0.6.1 → arraykit-0.6.2}/src/py.typed +0 -0
  18. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_array_go.py +0 -0
  19. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_block_index.py +0 -0
  20. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_delimited_to_arrays.py +0 -0
  21. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_delimited_to_arrays_integration.py +0 -0
  22. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_delimited_to_arrays_property.py +0 -0
  23. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_nonzero_1d_property.py +0 -0
  24. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_pyi.py +0 -0
  25. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_split_after_count.py +0 -0
  26. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_type_discovery.py +0 -0
  27. {arraykit-0.6.1 → arraykit-0.6.2}/test/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arraykit
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Array utilities for StaticFrame
5
5
  Home-page: https://github.com/static-frame/arraykit
6
6
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
@@ -37,6 +37,14 @@ ArrayKit requires the following:
37
37
  What is New in ArrayKit
38
38
  -------------------------
39
39
 
40
+ 0.6.2
41
+ ............
42
+
43
+ Extended ``nonzero_1d()`` to support non-contiguous arrays,
44
+
45
+ Optimizations to ``TriMap`` when mapping to object and flexible dtypes.
46
+
47
+
40
48
  0.6.1
41
49
  ............
42
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arraykit
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Array utilities for StaticFrame
5
5
  Home-page: https://github.com/static-frame/arraykit
6
6
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
@@ -5,7 +5,7 @@ from setuptools import Extension # type: ignore
5
5
  from setuptools import setup
6
6
  from pathlib import Path
7
7
 
8
- AK_VERSION = '0.6.1'
8
+ AK_VERSION = '0.6.2'
9
9
 
10
10
  def get_long_description() -> str:
11
11
  return '''The ArrayKit library provides utilities for creating and transforming NumPy arrays, implementing performance-critical StaticFrame operations as Python C extensions.
@@ -3535,7 +3535,7 @@ resolve_dtype_iter(PyObject *Py_UNUSED(m), PyObject *arg) {
3535
3535
  //------------------------------------------------------------------------------
3536
3536
  // general utility
3537
3537
 
3538
- #define NONZERO_APPEND_INDEX { \
3538
+ #define NONZERO_APPEND_INDEX_RELATIVE { \
3539
3539
  if (AK_UNLIKELY(count == capacity)) { \
3540
3540
  capacity <<= 1; \
3541
3541
  indices = (npy_int64*)realloc(indices, sizeof(npy_int64) * capacity);\
@@ -3546,7 +3546,18 @@ resolve_dtype_iter(PyObject *Py_UNUSED(m), PyObject *arg) {
3546
3546
  indices[count++] = p - p_start; \
3547
3547
  } \
3548
3548
 
3549
- // Given a Boolean, contiguous 1D array, return the index positions in an int64 array.
3549
+ #define NONZERO_APPEND_INDEX_ABSOLUTE { \
3550
+ if (AK_UNLIKELY(count == capacity)) { \
3551
+ capacity <<= 1; \
3552
+ indices = (npy_int64*)realloc(indices, sizeof(npy_int64) * capacity);\
3553
+ if (indices == NULL) { \
3554
+ return NULL; \
3555
+ } \
3556
+ } \
3557
+ indices[count++] = i; \
3558
+ } \
3559
+
3560
+ // Given a Boolean, contiguous 1D array, return the index positions in an int64 array. Through experimentation it has been verified that doing full-size allocation of memory does not permit outperforming NumPy at 10_000_000 scale; but doing less optimizations does help. Using bit masks does not improve perforamnce over pointer arithmetic. Prescanning for all empty is very effective. Note that NumPy befits from first counting the nonzeros, then allocating only enough data for the expexted number.
3550
3561
  static inline PyObject*
3551
3562
  AK_nonzero_1d(PyArrayObject* array) {
3552
3563
  // the maxiumum number of indices we could return is the size of the array; if this is under a certain number, probably better to just allocate that rather than reallocate
@@ -3566,112 +3577,71 @@ AK_nonzero_1d(PyArrayObject* array) {
3566
3577
  Py_ssize_t capacity = count_max < 1024 ? count_max : count_max / 8;
3567
3578
  npy_int64* indices = (npy_int64*)malloc(sizeof(npy_int64) * capacity);
3568
3579
 
3569
- // array is contiguous, 1d, boolean
3570
- npy_bool* p_start = (npy_bool*)PyArray_DATA(array);
3571
- npy_bool* p = p_start;
3572
- npy_bool* p_end = p + count_max;
3573
- npy_bool* p_end_roll = p_end - size_div.rem;
3574
-
3575
3580
  NPY_BEGIN_THREADS_DEF;
3576
3581
  NPY_BEGIN_THREADS;
3577
- // Through experimentation it has been verified that doing full-size allocation of memory does not permit outperforming NumPy at 10_000_000 scale; but doing less optimizations does help.
3578
- // Using bit masks does not improve perforamnce over pointer arithmetic.
3579
- // Prescanning for all empty is very effective.
3580
3582
 
3581
- while (p < p_end_roll) {
3582
- if (*(npy_uint64*)p == 0) {
3583
- p += 8; // no true within this 8 byte roll region
3584
- continue;
3583
+ if (PyArray_IS_C_CONTIGUOUS(array)) {
3584
+ npy_bool* p_start = (npy_bool*)PyArray_DATA(array);
3585
+ npy_bool* p = p_start;
3586
+ npy_bool* p_end = p + count_max;
3587
+ npy_bool* p_end_roll = p_end - size_div.rem;
3588
+
3589
+ while (p < p_end_roll) {
3590
+ if (*(npy_uint64*)p == 0) {
3591
+ p += 8; // no true within this 8 byte roll region
3592
+ continue;
3593
+ }
3594
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3595
+ p++;
3596
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3597
+ p++;
3598
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3599
+ p++;
3600
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3601
+ p++;
3602
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3603
+ p++;
3604
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3605
+ p++;
3606
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3607
+ p++;
3608
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3609
+ p++;
3610
+ }
3611
+ while (p < p_end) {
3612
+ if (*p) {NONZERO_APPEND_INDEX_RELATIVE;}
3613
+ p++;
3585
3614
  }
3586
- if (*p) {NONZERO_APPEND_INDEX;}
3587
- p++;
3588
- if (*p) {NONZERO_APPEND_INDEX;}
3589
- p++;
3590
- if (*p) {NONZERO_APPEND_INDEX;}
3591
- p++;
3592
- if (*p) {NONZERO_APPEND_INDEX;}
3593
- p++;
3594
- if (*p) {NONZERO_APPEND_INDEX;}
3595
- p++;
3596
- if (*p) {NONZERO_APPEND_INDEX;}
3597
- p++;
3598
- if (*p) {NONZERO_APPEND_INDEX;}
3599
- p++;
3600
- if (*p) {NONZERO_APPEND_INDEX;}
3601
- p++;
3602
3615
  }
3603
- while (p < p_end) {
3604
- if (*p) {NONZERO_APPEND_INDEX;}
3605
- p++;
3616
+ else {
3617
+ npy_intp i = 0; // position within Boolean array
3618
+ npy_intp i_end = count_max;
3619
+ npy_intp i_end_roll = count_max - size_div.rem;
3620
+ while (i < i_end_roll) {
3621
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3622
+ i++;
3623
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3624
+ i++;
3625
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3626
+ i++;
3627
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3628
+ i++;
3629
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3630
+ i++;
3631
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3632
+ i++;
3633
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3634
+ i++;
3635
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3636
+ i++;
3637
+ }
3638
+ while (i < i_end) {
3639
+ if (*(npy_bool*)PyArray_GETPTR1(array, i)) {NONZERO_APPEND_INDEX_ABSOLUTE;}
3640
+ i++;
3641
+ }
3606
3642
  }
3607
3643
  NPY_END_THREADS;
3608
3644
 
3609
- // npy_uint64 roll;
3610
- // while (p < p_end_roll) {
3611
- // roll = *(npy_uint64*)p;
3612
- // if (roll == 0) {
3613
- // p += 8; // no true within this 8 byte roll region
3614
- // continue;
3615
- // }
3616
- // // this order depends on byte order
3617
- // if (roll & 0xFF) {NONZERO_APPEND_OFFSET(0);}
3618
- // if (roll & 0xFF00) {NONZERO_APPEND_OFFSET(1);}
3619
- // if (roll & 0xFF0000) {NONZERO_APPEND_OFFSET(2);}
3620
- // if (roll & 0xFF000000) {NONZERO_APPEND_OFFSET(3);}
3621
- // if (roll & 0xFF00000000) {NONZERO_APPEND_OFFSET(4);}
3622
- // if (roll & 0xFF0000000000) {NONZERO_APPEND_OFFSET(5);}
3623
- // if (roll & 0xFF000000000000) {NONZERO_APPEND_OFFSET(6);}
3624
- // if (roll & 0xFF00000000000000) {NONZERO_APPEND_OFFSET(7);}
3625
- // p += 8;
3626
- // }
3627
- // while (p < p_end) {
3628
- // if (*p) {NONZERO_APPEND_OFFSET(0);}
3629
- // p++;
3630
- // }
3631
-
3632
-
3633
- // while (p < p_end_roll) {
3634
- // if (*(npy_uint64*)p == 0) {
3635
- // p += 8; // no true within this roll region
3636
- // continue;
3637
- // }
3638
- // if (AK_UNLIKELY(count + 8 >= capacity)) {
3639
- // capacity <<= 1;
3640
- // indices = (npy_int64*)realloc(indices, sizeof(npy_int64) * capacity);
3641
- // if (indices == NULL) {
3642
- // return NULL;
3643
- // }
3644
- // }
3645
- // if (*p) {indices[count++] = p - p_start;}
3646
- // p++;
3647
- // if (*p) {indices[count++] = p - p_start;}
3648
- // p++;
3649
- // if (*p) {indices[count++] = p - p_start;}
3650
- // p++;
3651
- // if (*p) {indices[count++] = p - p_start;}
3652
- // p++;
3653
- // if (*p) {indices[count++] = p - p_start;}
3654
- // p++;
3655
- // if (*p) {indices[count++] = p - p_start;}
3656
- // p++;
3657
- // if (*p) {indices[count++] = p - p_start;}
3658
- // p++;
3659
- // if (*p) {indices[count++] = p - p_start;}
3660
- // p++;
3661
- // }
3662
- // // at most three more indices remain
3663
- // if (AK_UNLIKELY(count + 7 >= capacity)) {
3664
- // capacity <<= 1;
3665
- // indices = (npy_int64*)realloc(indices, sizeof(npy_int64) * capacity);
3666
- // if (indices == NULL) {
3667
- // return NULL;
3668
- // }
3669
- // }
3670
- // while (p < p_end) {
3671
- // if (*p) {indices[count++] = p - p_start;}
3672
- // p++;
3673
- // }
3674
-
3675
3645
  npy_intp dims = {count};
3676
3646
  final = PyArray_SimpleNewFromData(1, &dims, NPY_INT64, (void*)indices);
3677
3647
  if (!final) {
@@ -3683,7 +3653,8 @@ AK_nonzero_1d(PyArrayObject* array) {
3683
3653
  PyArray_CLEARFLAGS((PyArrayObject*)final, NPY_ARRAY_WRITEABLE);
3684
3654
  return final;
3685
3655
  }
3686
- #undef NONZERO_APPEND_INDEX
3656
+ #undef NONZERO_APPEND_INDEX_RELATIVE
3657
+ #undef NONZERO_APPEND_INDEX_ABSOLUTE
3687
3658
 
3688
3659
  static PyObject*
3689
3660
  nonzero_1d(PyObject *Py_UNUSED(m), PyObject *a) {
@@ -3697,10 +3668,6 @@ nonzero_1d(PyObject *Py_UNUSED(m), PyObject *a) {
3697
3668
  PyErr_SetString(PyExc_ValueError, "Array must be of type bool");
3698
3669
  return NULL;
3699
3670
  }
3700
- if (!PyArray_IS_C_CONTIGUOUS(array)) {
3701
- PyErr_SetString(PyExc_ValueError, "Array must be contiguous");
3702
- return NULL;
3703
- }
3704
3671
  return AK_nonzero_1d(array);
3705
3672
  }
3706
3673
 
@@ -5869,8 +5836,6 @@ typedef struct TriMapObject {
5869
5836
  Py_ssize_t src_len;
5870
5837
  Py_ssize_t dst_len;
5871
5838
  Py_ssize_t len;
5872
- // Py_ssize_t src_connected;
5873
- // Py_ssize_t dst_connected;
5874
5839
  bool is_many;
5875
5840
  bool finalized;
5876
5841
 
@@ -5932,8 +5897,6 @@ TriMap_init(PyObject *self, PyObject *args, PyObject *kwargs) {
5932
5897
  tm->is_many = false;
5933
5898
  tm->finalized = false;
5934
5899
  tm->len = 0;
5935
- // tm->src_connected = 0;
5936
- // tm->dst_connected = 0;
5937
5900
 
5938
5901
  // we create arrays, and also pre-extract pointers to array data for fast insertion; we keep the array for optimal summing routines
5939
5902
  npy_intp dims_src_len[] = {src_len};
@@ -6039,7 +6002,7 @@ TriMap_repr(TriMapObject *self) {
6039
6002
  is_finalized);
6040
6003
  }
6041
6004
 
6042
- // Provide the integer positions connecting the `src` to the `dst`. If there is no match to `src` or `dst`, the unmatched position can be provided with -1. From each side, a connection is documented to the current `len`. Each time this is called `len` is incremented, indicating the inrease in position of th `final`. Return NULL on error
6005
+ // Provide the integer positions connecting the `src` to the `dst`. If there is no match to `src` or `dst`, the unmatched position can be provided with -1. From each side, a connection is documented to the current `len`. Each time this is called `len` is incremented, indicating the inrease in position in the `final`. Return NULL on error.
6043
6006
 
6044
6007
  // Inner function for calling from C; returns 0 on success, -1 on error. Exceptions will be set on error.
6045
6008
  static inline int
@@ -6062,7 +6025,6 @@ AK_TM_register_one(TriMapObject* tm, Py_ssize_t src_from, Py_ssize_t dst_from) {
6062
6025
  }
6063
6026
  tm->src_one[tm->src_one_count] = (TriMapOne){src_from, tm->len};
6064
6027
  tm->src_one_count += 1;
6065
- // tm->src_connected += 1;
6066
6028
  }
6067
6029
  if (dst_matched) {
6068
6030
  if (AK_UNLIKELY(tm->dst_one_count == tm->dst_one_capacity)) {
@@ -6076,7 +6038,6 @@ AK_TM_register_one(TriMapObject* tm, Py_ssize_t src_from, Py_ssize_t dst_from) {
6076
6038
  }
6077
6039
  tm->dst_one[tm->dst_one_count] = (TriMapOne){dst_from, tm->len};
6078
6040
  tm->dst_one_count += 1;
6079
- // tm->dst_connected += 1;
6080
6041
  }
6081
6042
  if (src_matched && dst_matched) {
6082
6043
  if (!tm->is_many) {
@@ -6215,8 +6176,6 @@ TriMap_register_many(TriMapObject *self, PyObject *args) {
6215
6176
  npy_int64 pos = *(npy_int64*)PyArray_GETPTR1(dst_from, i); // always int64
6216
6177
  self->dst_match_data[pos] = NPY_TRUE;
6217
6178
  }
6218
- // self->src_connected += increment;
6219
- // self->dst_connected += increment;
6220
6179
  self->len += increment;
6221
6180
  self->is_many = true;
6222
6181
  Py_RETURN_NONE;
@@ -6359,7 +6318,7 @@ TriMap_dst_no_fill(TriMapObject *self, PyObject *Py_UNUSED(unused)) {
6359
6318
  Py_RETURN_FALSE;
6360
6319
  }
6361
6320
 
6362
- # define TRANSFER_SCALARS(npy_type_to, npy_type_from) { \
6321
+ # define AK_TM_TRANSFER_SCALAR(npy_type_to, npy_type_from) do { \
6363
6322
  npy_type_to* array_to_data = (npy_type_to*)PyArray_DATA(array_to); \
6364
6323
  TriMapOne* o = one_pairs; \
6365
6324
  TriMapOne* o_end = o + one_count; \
@@ -6396,57 +6355,11 @@ TriMap_dst_no_fill(TriMapObject *self, PyObject *Py_UNUSED(unused)) {
6396
6355
  } \
6397
6356
  } \
6398
6357
  } \
6399
- } \
6400
-
6401
- // The elsize of the `to` array will be equal to or greater than the from array
6402
- #define TRANSFER_FLEXIBLE(c_type) { \
6403
- npy_intp t_element_size = PyArray_DESCR(array_to)->elsize; \
6404
- npy_intp t_element_cp = t_element_size / sizeof(c_type); \
6405
- npy_intp f_element_size = PyArray_DESCR(array_from)->elsize; \
6406
- npy_intp f_element_cp = f_element_size / sizeof(c_type); \
6407
- npy_intp gap = t_element_size - f_element_size; \
6408
- c_type* array_to_data = (c_type*)PyArray_DATA(array_to); \
6409
- c_type* f; \
6410
- c_type* t; \
6411
- c_type* t_end; \
6412
- npy_intp dst_pos; \
6413
- npy_int64 f_pos; \
6414
- PyArrayObject* dst; \
6415
- TriMapOne* o = one_pairs; \
6416
- TriMapOne* o_end = o + one_count; \
6417
- for (; o < o_end; o++) { \
6418
- f = (c_type*)PyArray_GETPTR1(array_from, o->from); \
6419
- t = array_to_data + t_element_cp * o->to; \
6420
- memcpy(t, f, f_element_size); \
6421
- memset(t + f_element_cp, '\0', gap); \
6422
- } \
6423
- for (Py_ssize_t i = 0; i < tm->many_count; i++) { \
6424
- t = array_to_data + t_element_cp * tm->many_to[i].start; \
6425
- t_end = array_to_data + t_element_cp * tm->many_to[i].stop; \
6426
- if (from_src) { \
6427
- f = (c_type*)PyArray_GETPTR1(array_from, tm->many_from[i].src);\
6428
- for (; t < t_end; t += t_element_cp) { \
6429
- memcpy(t, f, f_element_size); \
6430
- memset(t + f_element_cp, '\0', gap); \
6431
- } \
6432
- } \
6433
- else { \
6434
- dst_pos = 0; \
6435
- dst = tm->many_from[i].dst; \
6436
- for (; t < t_end; t += t_element_cp) { \
6437
- f_pos = *(npy_int64*)PyArray_GETPTR1(dst, dst_pos); \
6438
- f = (c_type*)PyArray_GETPTR1(array_from, f_pos); \
6439
- memcpy(t, f, f_element_size); \
6440
- memset(t + f_element_cp, '\0', gap); \
6441
- dst_pos++; \
6442
- } \
6443
- } \
6444
- } \
6445
- } \
6358
+ } while (0) \
6446
6359
 
6447
6360
  // Based on `tm` state, transfer from src or from dst (depending on `from_src`) to a `array_to`, a newly created contiguous array that is compatible with the values in `array_from`. Returns -1 on error. This only needs to match to / from type combinations that are possible from `resolve_dtype`, i.e., bool never goes to integer.
6448
6361
  static inline int
6449
- AK_TM_transfer(TriMapObject* tm,
6362
+ AK_TM_transfer_scalar(TriMapObject* tm,
6450
6363
  bool from_src,
6451
6364
  PyArrayObject* array_from,
6452
6365
  PyArrayObject* array_to) {
@@ -6455,206 +6368,194 @@ AK_TM_transfer(TriMapObject* tm,
6455
6368
 
6456
6369
  switch(PyArray_TYPE(array_to)){
6457
6370
  case NPY_BOOL:
6458
- TRANSFER_SCALARS(npy_bool, npy_bool);
6371
+ AK_TM_TRANSFER_SCALAR(npy_bool, npy_bool);
6459
6372
  return 0;
6460
6373
  case NPY_INT64:
6461
6374
  switch (PyArray_TYPE(array_from)) {
6462
6375
  case NPY_INT64:
6463
- TRANSFER_SCALARS(npy_int64, npy_int64);
6376
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_int64);
6464
6377
  return 0;
6465
6378
  case NPY_INT32:
6466
- TRANSFER_SCALARS(npy_int64, npy_int32);
6379
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_int32);
6467
6380
  return 0;
6468
6381
  case NPY_INT16:
6469
- TRANSFER_SCALARS(npy_int64, npy_int16);
6382
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_int16);
6470
6383
  return 0;
6471
6384
  case NPY_INT8:
6472
- TRANSFER_SCALARS(npy_int64, npy_int8);
6385
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_int8);
6473
6386
  return 0;
6474
6387
  case NPY_UINT32:
6475
- TRANSFER_SCALARS(npy_int64, npy_uint32);
6388
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_uint32);
6476
6389
  return 0;
6477
6390
  case NPY_UINT16:
6478
- TRANSFER_SCALARS(npy_int64, npy_uint16);
6391
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_uint16);
6479
6392
  return 0;
6480
6393
  case NPY_UINT8:
6481
- TRANSFER_SCALARS(npy_int64, npy_uint8);
6394
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_uint8);
6482
6395
  return 0;
6483
6396
  }
6484
6397
  break;
6485
6398
  case NPY_INT32:
6486
6399
  switch (PyArray_TYPE(array_from)) {
6487
6400
  case NPY_INT32:
6488
- TRANSFER_SCALARS(npy_int32, npy_int32);
6401
+ AK_TM_TRANSFER_SCALAR(npy_int32, npy_int32);
6489
6402
  return 0;
6490
6403
  case NPY_INT16:
6491
- TRANSFER_SCALARS(npy_int32, npy_int16);
6404
+ AK_TM_TRANSFER_SCALAR(npy_int32, npy_int16);
6492
6405
  return 0;
6493
6406
  case NPY_INT8:
6494
- TRANSFER_SCALARS(npy_int32, npy_int8);
6407
+ AK_TM_TRANSFER_SCALAR(npy_int32, npy_int8);
6495
6408
  return 0;
6496
6409
  case NPY_UINT16:
6497
- TRANSFER_SCALARS(npy_int32, npy_uint16);
6410
+ AK_TM_TRANSFER_SCALAR(npy_int32, npy_uint16);
6498
6411
  return 0;
6499
6412
  case NPY_UINT8:
6500
- TRANSFER_SCALARS(npy_int32, npy_uint8);
6413
+ AK_TM_TRANSFER_SCALAR(npy_int32, npy_uint8);
6501
6414
  return 0;
6502
6415
  }
6503
6416
  break;
6504
6417
  case NPY_INT16:
6505
6418
  switch (PyArray_TYPE(array_from)) {
6506
6419
  case NPY_INT16:
6507
- TRANSFER_SCALARS(npy_int16, npy_int16);
6420
+ AK_TM_TRANSFER_SCALAR(npy_int16, npy_int16);
6508
6421
  return 0;
6509
6422
  case NPY_INT8:
6510
- TRANSFER_SCALARS(npy_int16, npy_int8);
6423
+ AK_TM_TRANSFER_SCALAR(npy_int16, npy_int8);
6511
6424
  return 0;
6512
6425
  case NPY_UINT8:
6513
- TRANSFER_SCALARS(npy_int16, npy_uint8);
6426
+ AK_TM_TRANSFER_SCALAR(npy_int16, npy_uint8);
6514
6427
  return 0;
6515
6428
  }
6516
6429
  break;
6517
6430
  case NPY_INT8:
6518
- TRANSFER_SCALARS(npy_int8, npy_int8);
6431
+ AK_TM_TRANSFER_SCALAR(npy_int8, npy_int8);
6519
6432
  return 0;
6520
6433
  case NPY_UINT64:
6521
6434
  switch (PyArray_TYPE(array_from)) {
6522
6435
  case NPY_UINT64:
6523
- TRANSFER_SCALARS(npy_uint64, npy_uint64);
6436
+ AK_TM_TRANSFER_SCALAR(npy_uint64, npy_uint64);
6524
6437
  return 0;
6525
6438
  case NPY_UINT32:
6526
- TRANSFER_SCALARS(npy_uint64, npy_uint32);
6439
+ AK_TM_TRANSFER_SCALAR(npy_uint64, npy_uint32);
6527
6440
  return 0;
6528
6441
  case NPY_UINT16:
6529
- TRANSFER_SCALARS(npy_uint64, npy_uint16);
6442
+ AK_TM_TRANSFER_SCALAR(npy_uint64, npy_uint16);
6530
6443
  return 0;
6531
6444
  case NPY_UINT8:
6532
- TRANSFER_SCALARS(npy_uint64, npy_uint8);
6445
+ AK_TM_TRANSFER_SCALAR(npy_uint64, npy_uint8);
6533
6446
  return 0;
6534
6447
  }
6535
6448
  break;
6536
6449
  case NPY_UINT32:
6537
6450
  switch (PyArray_TYPE(array_from)) {
6538
6451
  case NPY_UINT32:
6539
- TRANSFER_SCALARS(npy_uint32, npy_uint32);
6452
+ AK_TM_TRANSFER_SCALAR(npy_uint32, npy_uint32);
6540
6453
  return 0;
6541
6454
  case NPY_UINT16:
6542
- TRANSFER_SCALARS(npy_uint32, npy_uint16);
6455
+ AK_TM_TRANSFER_SCALAR(npy_uint32, npy_uint16);
6543
6456
  return 0;
6544
6457
  case NPY_UINT8:
6545
- TRANSFER_SCALARS(npy_uint32, npy_uint8);
6458
+ AK_TM_TRANSFER_SCALAR(npy_uint32, npy_uint8);
6546
6459
  return 0;
6547
6460
  }
6548
6461
  break;
6549
6462
  case NPY_UINT16:
6550
6463
  switch (PyArray_TYPE(array_from)) {
6551
6464
  case NPY_UINT16:
6552
- TRANSFER_SCALARS(npy_uint16, npy_uint16);
6465
+ AK_TM_TRANSFER_SCALAR(npy_uint16, npy_uint16);
6553
6466
  return 0;
6554
6467
  case NPY_UINT8:
6555
- TRANSFER_SCALARS(npy_uint16, npy_uint8);
6468
+ AK_TM_TRANSFER_SCALAR(npy_uint16, npy_uint8);
6556
6469
  return 0;
6557
6470
  }
6558
6471
  break;
6559
6472
  case NPY_UINT8:
6560
- TRANSFER_SCALARS(npy_uint8, npy_uint8);
6473
+ AK_TM_TRANSFER_SCALAR(npy_uint8, npy_uint8);
6561
6474
  return 0;
6562
6475
  case NPY_FLOAT64:
6563
6476
  switch (PyArray_TYPE(array_from)) {
6564
6477
  case NPY_FLOAT64:
6565
- TRANSFER_SCALARS(npy_float64, npy_float64);
6478
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_float64);
6566
6479
  return 0;
6567
6480
  case NPY_FLOAT32:
6568
- TRANSFER_SCALARS(npy_float64, npy_float32);
6481
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_float32);
6569
6482
  return 0;
6570
6483
  case NPY_FLOAT16:
6571
- TRANSFER_SCALARS(npy_float64, npy_float16);
6484
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_float16);
6572
6485
  return 0;
6573
6486
  case NPY_INT64:
6574
- TRANSFER_SCALARS(npy_float64, npy_int64);
6487
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_int64);
6575
6488
  return 0;
6576
6489
  case NPY_INT32:
6577
- TRANSFER_SCALARS(npy_float64, npy_int32);
6490
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_int32);
6578
6491
  return 0;
6579
6492
  case NPY_INT16:
6580
- TRANSFER_SCALARS(npy_float64, npy_int16);
6493
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_int16);
6581
6494
  return 0;
6582
6495
  case NPY_INT8:
6583
- TRANSFER_SCALARS(npy_float64, npy_int8);
6496
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_int8);
6584
6497
  return 0;
6585
6498
  case NPY_UINT64:
6586
- TRANSFER_SCALARS(npy_float64, npy_uint64);
6499
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_uint64);
6587
6500
  return 0;
6588
6501
  case NPY_UINT32:
6589
- TRANSFER_SCALARS(npy_float64, npy_uint32);
6502
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_uint32);
6590
6503
  return 0;
6591
6504
  case NPY_UINT16:
6592
- TRANSFER_SCALARS(npy_float64, npy_uint16);
6505
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_uint16);
6593
6506
  return 0;
6594
6507
  case NPY_UINT8:
6595
- TRANSFER_SCALARS(npy_float64, npy_uint8);
6508
+ AK_TM_TRANSFER_SCALAR(npy_float64, npy_uint8);
6596
6509
  return 0;
6597
6510
  }
6598
6511
  break;
6599
6512
  case NPY_FLOAT32:
6600
6513
  switch (PyArray_TYPE(array_from)) {
6601
6514
  case NPY_FLOAT32:
6602
- TRANSFER_SCALARS(npy_float32, npy_float32);
6515
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_float32);
6603
6516
  return 0;
6604
6517
  case NPY_FLOAT16:
6605
- TRANSFER_SCALARS(npy_float32, npy_float16);
6518
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_float16);
6606
6519
  return 0;
6607
6520
  case NPY_INT16:
6608
- TRANSFER_SCALARS(npy_float32, npy_int16);
6521
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_int16);
6609
6522
  return 0;
6610
6523
  case NPY_INT8:
6611
- TRANSFER_SCALARS(npy_float32, npy_int8);
6524
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_int8);
6612
6525
  return 0;
6613
6526
  case NPY_UINT16:
6614
- TRANSFER_SCALARS(npy_float32, npy_uint16);
6527
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_uint16);
6615
6528
  return 0;
6616
6529
  case NPY_UINT8:
6617
- TRANSFER_SCALARS(npy_float32, npy_uint8);
6530
+ AK_TM_TRANSFER_SCALAR(npy_float32, npy_uint8);
6618
6531
  return 0;
6619
6532
  }
6620
6533
  break;
6621
6534
  case NPY_FLOAT16:
6622
6535
  switch (PyArray_TYPE(array_from)) {
6623
6536
  case NPY_FLOAT16:
6624
- TRANSFER_SCALARS(npy_float16, npy_float16);
6537
+ AK_TM_TRANSFER_SCALAR(npy_float16, npy_float16);
6625
6538
  return 0;
6626
6539
  case NPY_INT8:
6627
- TRANSFER_SCALARS(npy_float16, npy_int8);
6540
+ AK_TM_TRANSFER_SCALAR(npy_float16, npy_int8);
6628
6541
  return 0;
6629
6542
  case NPY_UINT16:
6630
- TRANSFER_SCALARS(npy_float16, npy_uint16);
6543
+ AK_TM_TRANSFER_SCALAR(npy_float16, npy_uint16);
6631
6544
  return 0;
6632
6545
  case NPY_UINT8:
6633
- TRANSFER_SCALARS(npy_float16, npy_uint8);
6546
+ AK_TM_TRANSFER_SCALAR(npy_float16, npy_uint8);
6634
6547
  return 0;
6635
6548
  }
6636
6549
  break;
6637
- case NPY_UNICODE: {
6638
- TRANSFER_FLEXIBLE(Py_UCS4);
6639
- return 0;
6640
- }
6641
- case NPY_STRING: {
6642
- TRANSFER_FLEXIBLE(char);
6643
- return 0;
6644
- }
6645
6550
  case NPY_DATETIME: {
6646
- TRANSFER_SCALARS(npy_int64, npy_int64);
6551
+ AK_TM_TRANSFER_SCALAR(npy_int64, npy_int64);
6647
6552
  return 0;
6648
6553
  }
6649
6554
  }
6650
- // AK_DEBUG_MSG_OBJ("array_to", (PyObject*)array_to);
6651
- // AK_DEBUG_MSG_OBJ("array_from", (PyObject*)array_from);
6652
6555
  PyErr_SetString(PyExc_TypeError, "No handling for types");
6653
6556
  return -1;
6654
6557
  }
6655
-
6656
- #undef TRANSFER_SCALARS
6657
- #undef TRANSFER_FLEXIBLE
6558
+ #undef AK_TM_TRANSFER_SCALAR
6658
6559
 
6659
6560
  // Returns -1 on error. Specialized transfer from any type of an array to an object array.
6660
6561
  static inline int
@@ -6740,12 +6641,10 @@ AK_TM_fill_object(TriMapObject* tm,
6740
6641
 
6741
6642
  PyArrayObject* final_fill = (PyArrayObject*)(from_src
6742
6643
  ? tm->final_src_fill : tm->final_dst_fill);
6743
-
6744
6644
  PyObject** array_to_data = (PyObject**)PyArray_DATA(array_to);
6745
6645
  npy_int64* p = (npy_int64*)PyArray_DATA(final_fill);
6746
6646
  npy_int64* p_end = p + PyArray_SIZE(final_fill);
6747
6647
  PyObject** target;
6748
- // npy_int64 pos;
6749
6648
  while (p < p_end) {
6750
6649
  target = array_to_data + *p++;
6751
6650
  Py_INCREF(fill_value);
@@ -6754,29 +6653,117 @@ AK_TM_fill_object(TriMapObject* tm,
6754
6653
  return 0;
6755
6654
  }
6756
6655
 
6757
- // TODO: AK_TM_fill_flexible
6758
- // this manually inserts string
6759
- // if (t_is_flexible) {
6760
- // // insert fill values
6761
- // Py_UCS4* t = (Py_UCS4*)PyArray_DATA(array_to);
6762
- // npy_intp t_cp = PyArray_DESCR(array_to)->elsize / UCS4_SIZE;
6763
- // Py_ssize_t len = PyUnicode_GET_LENGTH(fill_value) * UCS4_SIZE; // code points
6764
- // Py_ssize_t count = from_src ? tm->src_len : tm->dst_len;
6765
- // // NOTE: matches do not tell where a fill is needed
6766
- // npy_bool* d = from_src ? tm->src_match_data : tm->dst_match_data;
6767
- // npy_bool* d_end = d + count;
6768
- // while (d < d_end) {
6769
- // if (*d == NPY_FALSE) {
6770
- // if (PyUnicode_AsUCS4(fill_value, t, len, 0) == NULL) {
6771
- // Py_DECREF((PyObject*)array_to);
6772
- // return NULL;
6773
- // }
6774
- // }
6775
- // t += t_cp;
6776
- // d++;
6777
- // }
6778
- // }
6656
+ #define AK_TM_TRANSFER_FLEXIBLE(c_type) do { \
6657
+ Py_ssize_t one_count = from_src ? tm->src_one_count : tm->dst_one_count;\
6658
+ TriMapOne* one_pairs = from_src ? tm->src_one : tm->dst_one; \
6659
+ npy_intp t_element_size = PyArray_DESCR(array_to)->elsize; \
6660
+ npy_intp t_element_cp = t_element_size / sizeof(c_type); \
6661
+ npy_intp f_element_size = PyArray_DESCR(array_from)->elsize; \
6662
+ c_type* array_to_data = (c_type*)PyArray_DATA(array_to); \
6663
+ c_type* f; \
6664
+ c_type* t; \
6665
+ c_type* t_end; \
6666
+ npy_intp dst_pos; \
6667
+ npy_int64 f_pos; \
6668
+ PyArrayObject* dst; \
6669
+ TriMapOne* o = one_pairs; \
6670
+ TriMapOne* o_end = o + one_count; \
6671
+ for (; o < o_end; o++) { \
6672
+ f = (c_type*)PyArray_GETPTR1(array_from, o->from); \
6673
+ t = array_to_data + t_element_cp * o->to; \
6674
+ memcpy(t, f, f_element_size); \
6675
+ } \
6676
+ for (Py_ssize_t i = 0; i < tm->many_count; i++) { \
6677
+ t = array_to_data + t_element_cp * tm->many_to[i].start; \
6678
+ t_end = array_to_data + t_element_cp * tm->many_to[i].stop; \
6679
+ if (from_src) { \
6680
+ f = (c_type*)PyArray_GETPTR1(array_from, tm->many_from[i].src);\
6681
+ for (; t < t_end; t += t_element_cp) { \
6682
+ memcpy(t, f, f_element_size); \
6683
+ } \
6684
+ } \
6685
+ else { \
6686
+ dst_pos = 0; \
6687
+ dst = tm->many_from[i].dst; \
6688
+ for (; t < t_end; t += t_element_cp) { \
6689
+ f_pos = *(npy_int64*)PyArray_GETPTR1(dst, dst_pos); \
6690
+ f = (c_type*)PyArray_GETPTR1(array_from, f_pos); \
6691
+ memcpy(t, f, f_element_size); \
6692
+ dst_pos++; \
6693
+ } \
6694
+ } \
6695
+ } \
6696
+ } while (0) \
6697
+
6698
+ // Returns -1 on error.
6699
+ static inline int
6700
+ AK_TM_fill_unicode(TriMapObject* tm,
6701
+ bool from_src,
6702
+ PyArrayObject* array_to,
6703
+ PyObject* fill_value) {
6704
+ PyArrayObject* final_fill = (PyArrayObject*)(from_src
6705
+ ? tm->final_src_fill : tm->final_dst_fill);
6706
+
6707
+ Py_UCS4* array_to_data = (Py_UCS4*)PyArray_DATA(array_to);
6708
+ // code points per element
6709
+ npy_intp cp = PyArray_DESCR(array_to)->elsize / UCS4_SIZE;
6710
+
6711
+ bool decref_fill_value = false;
6712
+ if (PyBytes_Check(fill_value)) {
6713
+ fill_value = PyUnicode_FromEncodedObject(fill_value, "utf-8", NULL);
6714
+ if (fill_value == NULL) {
6715
+ return -1;
6716
+ }
6717
+ decref_fill_value = true;
6718
+ }
6719
+ else if (!PyUnicode_Check(fill_value)) {
6720
+ return -1;
6721
+ }
6722
+ Py_ssize_t fill_cp = PyUnicode_GET_LENGTH(fill_value) * UCS4_SIZE; // code points
6723
+ // p is the index position to fill
6724
+ npy_int64* p = (npy_int64*)PyArray_DATA(final_fill);
6725
+ npy_int64* p_end = p + PyArray_SIZE(final_fill);
6726
+ Py_UCS4* target;
6727
+ while (p < p_end) {
6728
+ target = array_to_data + (*p * cp);
6729
+ // disabling copying a null
6730
+ if (PyUnicode_AsUCS4(fill_value, target, fill_cp, 0) == NULL) {
6731
+ return -1;
6732
+ }
6733
+ p++;
6734
+ }
6735
+ if (decref_fill_value) {
6736
+ Py_DECREF(fill_value);
6737
+ }
6738
+ return 0;
6739
+ }
6779
6740
 
6741
+ // Returns -1 on error.
6742
+ static inline int
6743
+ AK_TM_fill_string(TriMapObject* tm,
6744
+ bool from_src,
6745
+ PyArrayObject* array_to,
6746
+ PyObject* fill_value) {
6747
+ PyArrayObject* final_fill = (PyArrayObject*)(from_src
6748
+ ? tm->final_src_fill : tm->final_dst_fill);
6749
+
6750
+ char* array_to_data = (char*)PyArray_DATA(array_to);
6751
+ npy_intp cp = PyArray_DESCR(array_to)->elsize;
6752
+ if (!PyBytes_Check(fill_value)) {
6753
+ return -1;
6754
+ }
6755
+ Py_ssize_t fill_cp = PyBytes_GET_SIZE(fill_value);
6756
+ const char* fill_data = PyBytes_AS_STRING(fill_value);
6757
+ // p is the index position to fill
6758
+ npy_int64* p = (npy_int64*)PyArray_DATA(final_fill);
6759
+ npy_int64* p_end = p + PyArray_SIZE(final_fill);
6760
+ char* target;
6761
+ while (p < p_end) {
6762
+ target = array_to_data + (*p++ * cp);
6763
+ memcpy(target, fill_data, fill_cp);
6764
+ }
6765
+ return 0;
6766
+ }
6780
6767
 
6781
6768
  // Returns NULL on error.
6782
6769
  static inline PyObject*
@@ -6790,6 +6777,9 @@ AK_TM_map_no_fill(TriMapObject* tm,
6790
6777
  npy_intp dims[] = {tm->len};
6791
6778
  PyArrayObject* array_to;
6792
6779
  bool dtype_is_obj = PyArray_TYPE(array_from) == NPY_OBJECT;
6780
+ bool dtype_is_unicode = PyArray_TYPE(array_from) == NPY_UNICODE;
6781
+ bool dtype_is_string = PyArray_TYPE(array_from) == NPY_STRING;
6782
+
6793
6783
  // create to array
6794
6784
  if (dtype_is_obj) { // initializes values to NULL
6795
6785
  array_to = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_OBJECT);
@@ -6810,8 +6800,14 @@ AK_TM_map_no_fill(TriMapObject* tm,
6810
6800
  return NULL;
6811
6801
  }
6812
6802
  }
6803
+ else if (dtype_is_unicode) {
6804
+ AK_TM_TRANSFER_FLEXIBLE(Py_UCS4);
6805
+ }
6806
+ else if (dtype_is_string) {
6807
+ AK_TM_TRANSFER_FLEXIBLE(char);
6808
+ }
6813
6809
  else {
6814
- if (AK_TM_transfer(tm, from_src, array_from, array_to)) {
6810
+ if (AK_TM_transfer_scalar(tm, from_src, array_from, array_to)) {
6815
6811
  Py_DECREF((PyObject*)array_to);
6816
6812
  return NULL;
6817
6813
  }
@@ -6864,15 +6860,22 @@ AK_TM_map_fill(TriMapObject* tm,
6864
6860
  // passing a borrowed ref; returns a new ref
6865
6861
  PyArray_Descr* dtype = AK_resolve_dtype(PyArray_DESCR(array_from), fill_value_dtype);
6866
6862
  bool dtype_is_obj = dtype->type_num == NPY_OBJECT;
6863
+ bool dtype_is_unicode = dtype->type_num == NPY_UNICODE;
6864
+ bool dtype_is_string = dtype->type_num == NPY_STRING;
6867
6865
 
6868
6866
  npy_intp dims[] = {tm->len};
6869
6867
  PyArrayObject* array_to;
6868
+
6870
6869
  if (dtype_is_obj) {
6871
6870
  Py_DECREF(dtype); // not needed
6872
6871
  // will initialize to NULL, not None
6873
6872
  array_to = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_OBJECT);
6874
6873
  Py_INCREF(array_from); // normalize refs when casting
6875
6874
  }
6875
+ else if (dtype_is_unicode || dtype_is_string) {
6876
+ array_to = (PyArrayObject*)PyArray_Zeros(1, dims, dtype, 0); // steals dtype ref
6877
+ Py_INCREF(array_from); // normalize refs when casting
6878
+ }
6876
6879
  else {
6877
6880
  array_to = (PyArrayObject*)PyArray_Empty(1, dims, dtype, 0); // steals dtype ref
6878
6881
  if (PyArray_TYPE(array_from) == NPY_DATETIME &&
@@ -6893,44 +6896,51 @@ AK_TM_map_fill(TriMapObject* tm,
6893
6896
  Py_DECREF((PyObject*)array_from);
6894
6897
  return NULL;
6895
6898
  }
6899
+ // array_from, array_to inc refed and dec refed on error
6896
6900
  if (dtype_is_obj) {
6897
6901
  if (AK_TM_transfer_object(tm, from_src, array_from, array_to)) {
6898
- Py_DECREF((PyObject*)array_to);
6899
- Py_DECREF((PyObject*)array_from);
6900
- return NULL;
6902
+ goto error;
6901
6903
  }
6902
6904
  if (AK_TM_fill_object(tm, from_src, array_to, fill_value)) {
6903
- Py_DECREF((PyObject*)array_to);
6904
- Py_DECREF((PyObject*)array_from);
6905
- return NULL;
6905
+ goto error;
6906
+ }
6907
+ }
6908
+ else if (dtype_is_unicode) {
6909
+ AK_TM_TRANSFER_FLEXIBLE(Py_UCS4);
6910
+ if (AK_TM_fill_unicode(tm, from_src, array_to, fill_value)) {
6911
+ goto error;
6912
+ }
6913
+ }
6914
+ else if (dtype_is_string) {
6915
+ AK_TM_TRANSFER_FLEXIBLE(char);
6916
+ if (AK_TM_fill_string(tm, from_src, array_to, fill_value)) {
6917
+ goto error;
6906
6918
  }
6907
6919
  }
6908
- // TODO: add special hanldig for unicode/bytes
6909
6920
  else {
6910
6921
  // Most simple is to fill with scalar, then overwrite values as needed; for object and flexible dtypes this is not efficient; for object dtypes, this obbligates us to decref the filled value when assigning
6911
6922
  if (PyArray_FillWithScalar(array_to, fill_value)) { // -1 on error
6912
- Py_DECREF((PyObject*)array_to);
6913
- Py_DECREF((PyObject*)array_from);
6914
- return NULL;
6923
+ goto error;
6915
6924
  }
6916
- if (AK_TM_transfer(tm, from_src, array_from, array_to)) {
6917
- Py_DECREF((PyObject*)array_to);
6918
- Py_DECREF((PyObject*)array_from);
6919
- return NULL;
6925
+ if (AK_TM_transfer_scalar(tm, from_src, array_from, array_to)) {
6926
+ goto error;
6920
6927
  }
6921
6928
  }
6922
-
6923
6929
  Py_DECREF((PyObject*)array_from); // ref inc for this function
6924
6930
  PyArray_CLEARFLAGS(array_to, NPY_ARRAY_WRITEABLE);
6925
6931
  return (PyObject*)array_to;
6932
+ error:
6933
+ Py_DECREF((PyObject*)array_to);
6934
+ Py_DECREF((PyObject*)array_from);
6935
+ return NULL;
6926
6936
  }
6937
+ #undef AK_TM_TRANSFER_FLEXIBLE
6927
6938
 
6928
6939
  static PyObject*
6929
6940
  TriMap_map_src_fill(TriMapObject *self, PyObject *args) {
6930
6941
  PyArrayObject* array_from;
6931
6942
  PyObject* fill_value;
6932
6943
  PyArray_Descr* fill_value_dtype;
6933
-
6934
6944
  if (!PyArg_ParseTuple(args,
6935
6945
  "O!OO!:map_src_fill",
6936
6946
  &PyArray_Type, &array_from,
@@ -6952,7 +6962,6 @@ TriMap_map_dst_fill(TriMapObject *self, PyObject *args) {
6952
6962
  PyArrayObject* array_from;
6953
6963
  PyObject* fill_value;
6954
6964
  PyArray_Descr* fill_value_dtype;
6955
-
6956
6965
  if (!PyArg_ParseTuple(args,
6957
6966
  "O!OO!:map_dst_fill",
6958
6967
  &PyArray_Type, &array_from,
@@ -6969,7 +6978,6 @@ TriMap_map_dst_fill(TriMapObject *self, PyObject *args) {
6969
6978
  return AK_TM_map_fill(self, from_src, array_from, fill_value, fill_value_dtype);
6970
6979
  }
6971
6980
 
6972
-
6973
6981
  static PyMethodDef TriMap_methods[] = {
6974
6982
  {"register_one", (PyCFunction)TriMap_register_one, METH_VARARGS, NULL},
6975
6983
  {"register_unmatched_dst", (PyCFunction)TriMap_register_unmatched_dst, METH_NOARGS, NULL},
@@ -83,4 +83,19 @@ class TestUnit(unittest.TestCase):
83
83
  a1[999] = True
84
84
  self.assertEqual(nonzero_1d(a1).tolist(), [999, 9_999_999])
85
85
  a1[0] = True
86
- self.assertEqual(nonzero_1d(a1).tolist(), [0, 999, 9_999_999])
86
+ self.assertEqual(nonzero_1d(a1).tolist(), [0, 999, 9_999_999])
87
+
88
+ def test_nonzero_1d_f(self) -> None:
89
+ # non-contiguous
90
+ a1 = np.arange(40).reshape(10, 4) % 3 == 0
91
+ a2 = a1[:, 3]
92
+ self.assertEqual(nonzero_1d(a2).tolist(), [0, 3, 6, 9])
93
+
94
+ a3 = a1[:, 1]
95
+ self.assertEqual(nonzero_1d(a3).tolist(), [2, 5, 8])
96
+
97
+ def test_nonzero_1d_g(self) -> None:
98
+ a1 = np.arange(20).reshape(4, 5) % 3 == 0
99
+ a2 = a1[:, 4]
100
+ # array([False, True, False, False])
101
+ self.assertEqual(nonzero_1d(a2).tolist(), [1])
@@ -975,6 +975,21 @@ class TestUnit(unittest.TestCase):
975
975
  post_dst = tm.map_dst_no_fill(dst)
976
976
  self.assertEqual(post_dst.tolist(), [b'a', b'bbb', b'cc', b'cc', b'dddd', b'a'])
977
977
 
978
+ def test_tri_map_map_bytes_a(self) -> None:
979
+ src = np.array([b'a', b'bbb', b'cc'], dtype=np.bytes_)
980
+ dst = np.array([b'cc', b'dddd', b'eee'], dtype=np.bytes_)
981
+
982
+ tm = TriMap(len(src), len(dst))
983
+ tm.register_one(0, -1)
984
+ tm.register_one(1, -1)
985
+ tm.register_one(2, 0)
986
+ tm.register_unmatched_dst()
987
+ tm.finalize()
988
+
989
+ post_src = tm.map_src_fill(src, b'--', np.dtype(np.bytes_))
990
+ post_dst = tm.map_dst_fill(dst, b'--', np.dtype(np.bytes_))
991
+ self.assertEqual(post_src.tolist(), [b'a', b'bbb', b'cc', b'--', b'--'])
992
+ self.assertEqual(post_dst.tolist(), [b'--', b'--', b'cc', b'dddd', b'eee'])
978
993
  #---------------------------------------------------------------------------
979
994
 
980
995
  def test_tri_map_map_unicode_a(self) -> None:
@@ -994,7 +1009,6 @@ class TestUnit(unittest.TestCase):
994
1009
  post_dst = tm.map_dst_fill(dst, '====', np.array('====').dtype)
995
1010
  self.assertEqual(post_dst.tolist(), ['a', 'a', 'a', '====', 'cc', 'cc', '===='])
996
1011
 
997
-
998
1012
  def test_tri_map_map_unicode_b(self) -> None:
999
1013
  src = np.array(['a', 'bbb', 'cc', 'dddd'])
1000
1014
  dst = np.array(['cc', 'a', 'a', 'a', 'cc'])
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes