windows-nt/Source/XPSP1/NT/base/ntos/rtl/array.c

517 lines
14 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) Microsoft Corporation
Module Name:
array.c
Abstract:
This module contains some slightly higher level functions
for dealing the arrays.
Author:
Jay Krell (JayKrell) April 2001
Revision History:
Environment:
anywhere
--*/
#include "ntrtlp.h"
//
// the type of the comparison callback used by qsort and bsearch,
// and optionally RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
//
typedef
int
(__cdecl *
RTL_QSORT_BSEARCH_COMPARISON_FUNCTION)(
CONST VOID * Element1,
CONST VOID * Element2
);
//
// the type of the comparison callback usually used by
// RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
//
typedef
RTL_GENERIC_COMPARE_RESULTS
(NTAPI *
RTL_COMPARE_ARRAY_ELEMENT_FUNCTION)(
PVOID Context,
IN CONST VOID * Element1,
IN CONST VOID * Element2,
IN SIZE_T ElementSize
);
//
// the callback to RtlMergeSortedArrays and RtlRemoveAdjacentEquivalentArrayElements
// to copy an elmement, if not via RtlCopyMemory
//
typedef
VOID
(NTAPI *
RTL_COPY_ARRAY_ELEMENT_FUNCTION)(
PVOID Context,
PVOID To,
CONST VOID * From,
IN SIZE_T ElementSize
);
#define \
RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE \
(0x00000001)
// deliberately not NTSYSAPI, so it can be linked to statically w/o warning
NTSTATUS
NTAPI
RtlMergeSortedArrays(
IN ULONG Flags,
IN CONST VOID * VoidArray1,
IN SIZE_T Count1,
IN CONST VOID * VoidArray2,
IN SIZE_T Count2,
// VoidResult == NULL is useful for getting the count first.
OUT PVOID VoidResult OPTIONAL,
OUT PSIZE_T OutResultCount,
IN SIZE_T ElementSize,
IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
PVOID CompareContext OPTIONAL,
IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL,
PVOID CopyContext OPTIONAL
);
#define \
RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE \
(0x00000001)
// deliberately not NTSYSAPI, so it can be linked to statically w/o warning
NTSTATUS
NTAPI
RtlRemoveAdjacentEquivalentArrayElements(
IN ULONG Flags,
IN OUT PVOID VoidArray,
IN SIZE_T Count,
OUT PSIZE_T OutCount,
IN SIZE_T ElementSize,
IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
PVOID CompareContext OPTIONAL,
IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL,
PVOID CopyContext OPTIONAL
);
typedef CONST VOID* PCVOID;
RTL_GENERIC_COMPARE_RESULTS
NTAPI
RtlpQsortBsearchCompareAdapter(
PVOID VoidContext,
IN PCVOID VoidElement1,
IN PCVOID VoidElement2,
IN SIZE_T ElementSize
)
/*++
Routine Description:
This function is an "adapter" used so people can use comparison functions intended
for use with qsort and bsearch with RtlMergeSortedArrays
and RtlRemoveAdjacentEquivalentArrayElements.
Arguments:
VoidContext - the actual qsort/bsearch comparison callback function
VoidElement1 - an elment to compare
VoidElement2 - another elment to compare
Return Value:
GenericLessThan
GenericGreaterThan
GenericEqual
--*/
{
CONST RTL_QSORT_BSEARCH_COMPARISON_FUNCTION QsortBsearchCompareCallback =
(RTL_QSORT_BSEARCH_COMPARISON_FUNCTION)VoidContext;
CONST int i = (*QsortBsearchCompareCallback)(VoidElement1, VoidElement2);
return (i < 0) ? GenericLessThan : (i > 0) ? GenericGreaterThan : GenericEqual;
}
VOID
NTAPI
RtlpDoNothingCopyArrayElement(
PVOID Context,
OUT PVOID To,
IN CONST VOID * From,
IN SIZE_T ElementSize
)
/*++
Routine Description:
RtlMergeSortedArrays uses this for CopyCallback when ResultArray==NULL, which
is useful for a two pass sequence that first determines
the size of the result, then allocates it, then writes it.
Arguments:
Context - ignored
To - ignored
From - ignored
Return Value:
none
--*/
{
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(To);
UNREFERENCED_PARAMETER(From);
// do nothing, deliberately
}
NTSTATUS
NTAPI
RtlRemoveAdjacentEquivalentArrayElements( // aka "unique" (if sorted)
IN ULONG Flags,
IN OUT PVOID VoidArray,
IN SIZE_T Count,
OUT PSIZE_T OutCount,
IN SIZE_T ElementSize,
IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
PVOID CompareContext OPTIONAL,
IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback OPTIONAL, // defaults to RtlCopyMemory
PVOID CopyContext OPTIONAL
)
/*++
Routine Description:
remove ajdacent equivalent elements from an array
aka "unique", if the array is sorted
Arguments:
VoidArray - the start of a array
Count - the number of elements in the array
ElementSize - the sizes of the elements in the array, in bytes
CompareCallback - a tristate comparison function in the style of qsort/bsearch
Return Value:
The return value is the number of elements contained
in the resulting array.
--*/
{
CONST PUCHAR Base = (PUCHAR)VoidArray;
CONST PUCHAR End = (PUCHAR)(Base + Count * ElementSize);
PUCHAR LastAccepted = Base;
PUCHAR NextAccepted = (Base + ElementSize);
PUCHAR Iterator = NextAccepted;
NTSTATUS Status;
if (OutCount == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
*OutCount = 0;
if ((Flags & ~RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if ((Flags & RTL_REMOVE_ADJACENT_EQUIVALENT_ARRAY_ELEMENTS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
CompareCallback = (RTL_COMPARE_ARRAY_ELEMENT_FUNCTION)RtlpQsortBsearchCompareAdapter;
CompareContext = (PVOID)CompareCallback;
}
if (Count < 2) {
*OutCount = 1;
Status = STATUS_SUCCESS;
goto Exit;
}
Count = 1; // always take the first element
for ( ; Iterator != End ; Iterator += ElementSize) {
if ((*CompareCallback)(CompareContext, Iterator, LastAccepted, ElementSize) != 0) {
if (Iterator != NextAccepted) {
if (CopyCallback != NULL) {
(*CopyCallback)(CopyContext, NextAccepted, Iterator, ElementSize);
} else {
RtlCopyMemory(NextAccepted, Iterator, ElementSize);
}
} else {
// do not bother copying until we have skipped any elements
}
LastAccepted = NextAccepted;
NextAccepted += ElementSize;
Count += 1;
}
}
*OutCount = Count;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
NTSTATUS
NTAPI
RtlMergeSortedArrays(
IN ULONG Flags,
IN PCVOID VoidArray1,
IN SIZE_T Count1,
IN PCVOID VoidArray2,
IN SIZE_T Count2,
OUT PVOID VoidResult OPTIONAL,
OUT PSIZE_T OutResultCount,
IN SIZE_T ElementSize,
IN RTL_COMPARE_ARRAY_ELEMENT_FUNCTION CompareCallback,
PVOID CompareContext OPTIONAL,
IN RTL_COPY_ARRAY_ELEMENT_FUNCTION CopyCallback,
PVOID CopyContext OPTIONAL
)
/*++
Routine Description:
Merge two sorted arrays into a third array.
The third array must be preallocated to the size of the sum
of the sizes of the two input arrays
Arguments:
VoidArray1 - the start of an array
Count1 - the number of elements in the array that starts at VoidArray1
VoidArray2 - the start of another array
Count2 - the number of elements in the array that starts at VoidArray2
VoidResult - the resulting array, must be capable of holding Count1+Count2 elements
if this parameter is not supplied, no copying is done and the just the
resulting required size is returned
ElementSize - the sizes of the elements in all the arrays, in bytes
CompareCallback - a tristate comparison function in the style of qsort/bsearch, plus it takes an additional parameter for context
Return Value:
The return value is the number of elements contained
in the resulting array VoidResult.
--*/
{
PUCHAR Array1 = (PUCHAR)VoidArray1;
PUCHAR Array2 = (PUCHAR)VoidArray2;
PUCHAR ResultArray = (PUCHAR)VoidResult;
SIZE_T ResultCount = 0;
NTSTATUS Status;
if (OutResultCount == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
*OutResultCount = 0;
if ((Flags & ~RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
//
// This is useful to get back the resulting count, before allocating
// the space.
//
if (VoidResult == NULL) {
CopyCallback = RtlpDoNothingCopyArrayElement;
}
if ((Flags & RTL_MERGE_SORTED_ARRAYS_FLAG_COMPARE_IS_QSORT_BSEARCH_SIGNATURE) != 0) {
CompareCallback = RtlpQsortBsearchCompareAdapter;
CompareContext = (PVOID)CompareCallback;
}
for ( ; Count1 != 0 && Count2 != 0 ; ) {
CONST int CompareResult = (*CompareCallback)(CompareContext, Array1, Array2, ElementSize);
if (CompareResult < 0) {
if (CopyCallback != NULL) {
(*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
} else {
RtlCopyMemory(ResultArray, Array1, ElementSize);
}
Array1 += ElementSize;
Count1 -= 1;
}
else if (CompareResult > 0) {
if (CopyCallback != NULL) {
(*CopyCallback)(CopyContext, ResultArray, Array2, ElementSize);
} else {
RtlCopyMemory(ResultArray, Array2, ElementSize);
}
Array2 += ElementSize;
Count2 -= 1;
}
else /* CompareResult == 0 */ {
//
// move past the elements in both arrays, chosing arbitrarily
// which one to take (Array1)
//
if (CopyCallback != NULL) {
(*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
} else {
RtlCopyMemory(ResultArray, Array1, ElementSize);
}
Array1 += ElementSize;
Array2 += ElementSize;
Count1 -= 1;
Count2 -= 1;
}
ResultCount += 1;
ResultArray += ElementSize;
}
//
// now pick up the tail of whichever one has any left, if either
//
if (VoidResult == NULL) {
ResultCount += Count1 + Count2;
} else if (Count1 != 0) {
ResultCount += Count1;
if (CopyCallback != NULL) {
while (Count1 != 0) {
//
// perhaps CopyCallback should be copy_n instead of copy_1,
// so we might gain an efficiency over the loop with an uninlinable
// calback..
//
(*CopyCallback)(CopyContext, ResultArray, Array1, ElementSize);
Count1 -= 1;
ResultArray += ElementSize;
Array1 += ElementSize;
}
} else {
RtlCopyMemory(ResultArray, Array1, Count1 * ElementSize);
ResultArray += Count1 * ElementSize;
}
} else if (Count2 != 0) {
ResultCount += Count2;
if (CopyCallback != NULL) {
while (Count2 != 0) {
//
// perhaps CopyCallback should be copy_n instead of copy_1
//
(*CopyCallback)(CopyContext, ResultArray, Array2, ElementSize);
Count2 -= 1;
ResultArray += ElementSize;
Array2 += ElementSize;
}
} else {
RtlCopyMemory(ResultArray, Array2, Count2 * ElementSize);
//optimize away ResultArray += Count2 * ElementSize;
}
}
*OutResultCount = ResultCount;
Status = STATUS_SUCCESS;
Exit:
return ResultCount;
}
#if 0 // test cases
int __cdecl CompareULONG(PCVOID v1, PCVOID v2)
{
ULONG i1 = *(ULONG*)v1;
ULONG i2 = *(ULONG*)v2;
if (i1 > i2)
return 1;
if (i1 < i2)
return -1;
return 0;
}
void RtlpTestUnique(const char * s)
{
ULONG rg[64];
ULONG i;
ULONG len = strlen(s);
for (i = 0 ; i != len ; ++i) rg[i] = s[i];
len = RtlRemoveAdjacentEquivalentArrayElements(1, rg, len, sizeof(rg[0]), (PVOID)CompareULONG, NULL, NULL, NULL);
printf("---");
for (i = 0 ; i != len ; ++i) printf("%lu ", rg[i]);
printf("---\n");
}
void RtlpTestMerge(const char * s, const char * t)
{
ULONG rg1[64];
ULONG rg2[64];
ULONG rg[128];
ULONG i;
ULONG slen = strlen(s);
ULONG tlen = strlen(t);
ULONG len;
printf("merge(");
for (i = 0 ; i != slen ; ++i) rg1[i] = s[i];
for (i = 0 ; i != slen ; ++i) printf("%lu ", rg1[i]);
printf(",");
for (i = 0 ; i != tlen ; ++i) rg2[i] = t[i];
for (i = 0 ; i != tlen ; ++i) printf("%lu ", rg2[i]);
len = RtlMergeSortedArrays(1, rg1, slen, rg2, tlen, rg, sizeof(rg[0]), (PVOID)CompareULONG, NULL, NULL, NULL);
printf(")=(---");
for (i = 0 ; i != len ; ++i) printf("%lu ", rg[i]);
printf(")\n");
}
int __cdecl main()
{
RtlpTestUnique("");
RtlpTestUnique("\1");
RtlpTestUnique("\1\2");
RtlpTestUnique("\1\2\3");
RtlpTestUnique("\1\1\2\3");
RtlpTestUnique("\1\2\2\3");
RtlpTestUnique("\1\2\3\3");
RtlpTestUnique("\1\1\1\2\2\2\2\2\3");
RtlpTestUnique("\1\1\1\2\3\3\3\3\3\3\3\3");
RtlpTestUnique("\1\2\2\2\2\2\2\3\3\3\3\3\3\3\3");
RtlpTestUnique("\1\1\1\1\1\2\2\2\2\2\3\3\3\3\3\3");
RtlpTestUnique("\1\1\1\1\1\2\2\2\2\2\3\3\3\3\3\3\1");
RtlpTestMerge("\1\2\3", "\4\5\6");
RtlpTestMerge("\1\3\5", "\2\4\6");
RtlpTestMerge("\1\2\3", "\2\4\6");
RtlpTestMerge("\1\1\1\2\3", "\2\4\6\6\6");
return 0;
}
#endif