HashLib4CSharp - Comprehensive Hashing Library written in C#

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
HashLib4CSharp is a comprehensive and easy to use hashing library written in C#. it provides flexible interfaces that makes usage a breeze. Hashing can be done in single pass or incremental mode. it also provides methods to clone the internal state of each hash object.
it provides Adapter classes that makes it easily pluggable to methods that accepts the Standard .NET HashAlgorithm, HMAC or DeriveBytes abstract base classes as the need may apply.

All functionality of the library is tested using the nunit framework.

This Library is built against NET Standard 2.1.

* Do note that this is a C# port of my HashLib4Pascal library with various improvements.

GitHub Repository URL
HashLib4CSharp
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
@Xor-el : Why did you define your own exceptions here:
https://github.com/Xor-el/HashLib4CSharp/blob/master/HashLib4CSharp/src/Utils/HashLibException.cs:
/*
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
{ *                             HashLib4CSharp Library                              * }
{ *                      Copyright (c) 2020 Ugochukwu Mmaduekwe                     * }
{ *                 GitHub Profile URL <https://github.com/Xor-el>                  * }

{ *  Distributed under the MIT software license, see the accompanying LICENSE file  * }
{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }

{ *                              Acknowledgements:                                  * }
{ *                                                                                 * }
{ *   This library was sponsored by Sphere 10 Software (https://www.sphere10.com)   * }
{ *         for the purposes of supporting the XXX (https://YYY) project.           * }
{ *                                                                                 * }
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
*/

using System;

namespace HashLib4CSharp.Utils
{
    public class HashLibException : Exception
    {
        protected HashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NullReferenceHashLibException : HashLibException
    {
        internal NullReferenceHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class InvalidOperationHashLibException : HashLibException
    {
        internal InvalidOperationHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class IndexOutOfRangeHashLibException : HashLibException
    {
        internal IndexOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public class ArgumentHashLibException : HashLibException
    {
        internal ArgumentHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentInvalidHashLibException : ArgumentHashLibException
    {
        internal ArgumentInvalidHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentNullHashLibException : ArgumentHashLibException
    {
        public ArgumentNullHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentOutOfRangeHashLibException : ArgumentHashLibException
    {
        internal ArgumentOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NotImplementedHashLibException : HashLibException
    {
        internal NotImplementedHashLibException(string message) : base(message)
        {
        }
    }

    public class IOHashLibException : HashLibException
    {
        internal IOHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class FileNotFoundHashLibException : IOHashLibException
    {
        internal FileNotFoundHashLibException(string message) : base(message)
        {
        }
    }
}

when the .NET Framework documentation says:
Choosing standard exceptions
When you have to throw an exception, you can often use an existing exception type in the .NET Framework instead of implementing a custom exception. You should use a standard exception type under these two conditions:
  • You are throwing an exception that is caused by a usage error (that is, by an error in program logic made by the developer who is calling your method). ...
  • You are handling an error that can be communicated to the caller with an existing .NET Framework exception. You should throw the most derived exception possible. ...
and
Implementing custom exceptions
In the following cases, using an existing .NET Framework exception to handle an error condition is not adequate:

  • When the exception reflects a unique program error that cannot be mapped to an existing .NET Framework exception.
  • When the exception requires handling that is different from the handling that is appropriate for an existing .NET Framework exception, or the exception must be disambiguated from a similar exception. For example, if you throw an ArgumentOutOfRangeException exception when parsing the numeric representation of a string that is out of range of the target integral type, you would not want to use the same exception for an error that results from the caller not supplying the appropriate constrained values when calling the method.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
Also there is a lot of use of unsafe pointers in the code. I've only done a brief scan so far, but I believe a majority of it can be replaced with using Span<byte> so that you don't have to compile with the unsafe flag on.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
Also there is a lot of use of unsafe pointers in the code. I've only done a brief scan so far, but I believe a majority of it can be replaced with using Span<byte> so that you don't have to compile with the unsafe flag on.

Any suggestions of an example where using span<byte> would be a better fit instead of unsafe code?
 

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
@Xor-el : Why did you define your own exceptions here:
https://github.com/Xor-el/HashLib4CSharp/blob/master/HashLib4CSharp/src/Utils/HashLibException.cs:
/*
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
{ *                             HashLib4CSharp Library                              * }
{ *                      Copyright (c) 2020 Ugochukwu Mmaduekwe                     * }
{ *                 GitHub Profile URL <https://github.com/Xor-el>                  * }

{ *  Distributed under the MIT software license, see the accompanying LICENSE file  * }
{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }

{ *                              Acknowledgements:                                  * }
{ *                                                                                 * }
{ *   This library was sponsored by Sphere 10 Software (https://www.sphere10.com)   * }
{ *         for the purposes of supporting the XXX (https://YYY) project.           * }
{ *                                                                                 * }
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
*/

using System;

namespace HashLib4CSharp.Utils
{
    public class HashLibException : Exception
    {
        protected HashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NullReferenceHashLibException : HashLibException
    {
        internal NullReferenceHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class InvalidOperationHashLibException : HashLibException
    {
        internal InvalidOperationHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class IndexOutOfRangeHashLibException : HashLibException
    {
        internal IndexOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public class ArgumentHashLibException : HashLibException
    {
        internal ArgumentHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentInvalidHashLibException : ArgumentHashLibException
    {
        internal ArgumentInvalidHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentNullHashLibException : ArgumentHashLibException
    {
        public ArgumentNullHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentOutOfRangeHashLibException : ArgumentHashLibException
    {
        internal ArgumentOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NotImplementedHashLibException : HashLibException
    {
        internal NotImplementedHashLibException(string message) : base(message)
        {
        }
    }

    public class IOHashLibException : HashLibException
    {
        internal IOHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class FileNotFoundHashLibException : IOHashLibException
    {
        internal FileNotFoundHashLibException(string message) : base(message)
        {
        }
    }
}

when the .NET Framework documentation says:

and
Thanks a lot for this suggestion, will implement it as indicated.
 

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
@Skydiver, for cases like this

"you would not want to use the same exception for an error that results from the caller not supplying the appropriate constrained values when calling the method"

What type of inbuilt exception do you recommend? Is "ArgumentException" okay for such scenario?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
I tend to use ArgumentException and put a more detailed message about why an argument was invalid. It only in the cases when I need to let the caller be able to distinguish between multiple different kinds of ArgumentException's that may result from a single line of code that I create a custom exception. That custom exception would still derive from the most specific built in exception that I can find, but I would create a subclass that even further distinguishes it. That way the caller can choose to catch a specific exception, or they can choose to catch a more generic exception.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
Can you please suggest an appropriate comment header style.
I would suggest just to keep it simple:
C#:
/*
HashLib4CSharp Library
Copyright (c) 2020 Ugochukwu Mmaduekwe
GitHub Profile URL <https://github.com/Xor-el>

Distributed under the MIT software license, see the accompanying LICENSE file
or visit http://www.opensource.org/licenses/mit-license.php.

Acknowledgements:
This library was sponsored by Sphere 10 Software (https://www.sphere10.com)
for the purposes of supporting the XXX (https://YYY) project.
*/
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
I saw that you picked up the 2 pull requests I sent last night regarding the performance test UI. I'll probably get around to doing a pull from your repo soon. I've been busy debugging the CRC32 code because my Span implementation was failing the unit tests. Just figured it about 5 minutes ago. I'll post here soon with a comparison of the unsafe vs. span code.
 

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
I saw that you picked up the 2 pull requests I sent last night regarding the performance test UI. I'll probably get around to doing a pull from your repo soon. I've been busy debugging the CRC32 code because my Span implementation was failing the unit tests. Just figured it about 5 minutes ago. I'll post here soon with a comparison of the unsafe vs. span code.
Yes I did, thanks for those and also thanks a lot for your time.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
So here is a version of the CRC32 computation using Span<T>:
Using Span and take out branches inside tight loop:
protected void LocalCrcCompute(uint[][] crcTable, byte[] data, int index, int length)
{
    if (data == null) throw new ArgumentNullHashLibException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;

    var crc = ~CurrentCRC;
    var leftovers = BitConverter.IsLittleEndian ? ComputeLittleEndianBlocks()
        : ComputeBigEndianBlocks();

    // remaining 1 to 63 bytes (standard algorithm)
    foreach (var b in leftovers)
        crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ b];

    CurrentCRC = ~crc;

    ReadOnlySpan<byte> ComputeLittleEndianBlocks()
    {
        var dataSpan = data.AsSpan(index, length);
        while (dataSpan.Length >= bytesAtOnce)
        {
            var dataUints = MemoryMarshal.Cast<byte, uint>(dataSpan);
            for (int unrolling = 0; unrolling < unroll; unrolling++, dataUints = dataUints.Slice(4))
            {
                var one = dataUints[0] ^ crc;
                var two = dataUints[1];
                var three = dataUints[2];
                var four = dataUints[3];

                crc = crcTable[0][(four >> 24) & 0xFF] ^
                    crcTable[1][(four >> 16) & 0xFF] ^
                    crcTable[2][(four >> 8) & 0xFF] ^
                    crcTable[3][four & 0xFF] ^
                    crcTable[4][(three >> 24) & 0xFF] ^
                    crcTable[5][(three >> 16) & 0xFF] ^
                    crcTable[6][(three >> 8) & 0xFF] ^
                    crcTable[7][three & 0xFF] ^
                    crcTable[8][(two >> 24) & 0xFF] ^
                    crcTable[9][(two >> 16) & 0xFF] ^
                    crcTable[10][(two >> 8) & 0xFF] ^
                    crcTable[11][two & 0xFF] ^
                    crcTable[12][(one >> 24) & 0xFF] ^
                    crcTable[13][(one >> 16) & 0xFF] ^
                    crcTable[14][(one >> 8) & 0xFF] ^
                    crcTable[15][one & 0xFF];
            }

            dataSpan = dataSpan.Slice(bytesAtOnce);
        }
        return dataSpan;
    }

    ReadOnlySpan<byte> ComputeBigEndianBlocks()
    {
        var dataSpan = data.AsSpan(index, length);
        while (dataSpan.Length >= bytesAtOnce)
        {
            var dataUints = MemoryMarshal.Cast<byte, uint>(dataSpan);
            for (int unrolling = 0; unrolling < unroll; unrolling++, dataUints = dataUints.Slice(4))
            {
                var one = dataUints[0] ^ Bits.ReverseBytesUInt32(crc);
                var two = dataUints[1];
                var three = dataUints[2];
                var four = dataUints[3];

                crc = crcTable[0][four & 0xFF] ^
                    crcTable[1][(four >> 8) & 0xFF] ^
                    crcTable[2][(four >> 16) & 0xFF] ^
                    crcTable[3][(four >> 24) & 0xFF] ^
                    crcTable[4][three & 0xFF] ^
                    crcTable[5][(three >> 8) & 0xFF] ^
                    crcTable[6][(three >> 16) & 0xFF] ^
                    crcTable[7][(three >> 24) & 0xFF] ^
                    crcTable[8][two & 0xFF] ^
                    crcTable[9][(two >> 8) & 0xFF] ^
                    crcTable[10][(two >> 16) & 0xFF] ^
                    crcTable[11][(two >> 24) & 0xFF] ^
                    crcTable[12][one & 0xFF] ^
                    crcTable[13][(one >> 8) & 0xFF] ^
                    crcTable[14][(one >> 16) & 0xFF] ^
                    crcTable[15][(one >> 24) & 0xFF];
            }

            dataSpan = dataSpan.Slice(bytesAtOnce);
        }
        return dataSpan;
    }
}

and here is the original code that used unsafe pointers as well the branch inside the loop:
Original unsafe code with pointers and branch inside tight loop:
protected unsafe void LocalCrcCompute(uint[][] crcTable, byte[] data, int index,
                                      int length)
{
    if (data == null) throw new ArgumentNullHashLibException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;
    var crc = ~CurrentCRC;

    fixed (byte* dataPtr = data)
    {
        var srcPtr = (uint*) (dataPtr + index);
        while (length >= bytesAtOnce)
        {
            var unrolling = 0;
            while (unrolling < unroll)
            {
                var one = Converters.ReadPCardinalAsUInt32(srcPtr)
                    ^ Converters.le2me_32(crc);
                srcPtr++;
                var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;
                var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;
                var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;

                if (BitConverter.IsLittleEndian)
                {
                    crc = crcTable[0][(four >> 24) & 0xFF] ^ crcTable[1]
                        [(four >> 16) & 0xFF] ^ crcTable[2][(four >> 8) & 0xFF]
                        ^ crcTable[3][four & 0xFF] ^ crcTable[4]
                        [(three >> 24) & 0xFF] ^ crcTable[5][(three >> 16) & 0xFF]
                        ^ crcTable[6][(three >> 8) & 0xFF] ^ crcTable[7]
                        [three & 0xFF] ^ crcTable[8][(two >> 24) & 0xFF] ^ crcTable
                        [9][(two >> 16) & 0xFF] ^ crcTable[10][(two >> 8) & 0xFF]
                        ^ crcTable[11][two & 0xFF] ^ crcTable[12][(one >> 24) & 0xFF]
                        ^ crcTable[13][(one >> 16) & 0xFF] ^ crcTable[14]
                        [(one >> 8) & 0xFF] ^ crcTable[15][one & 0xFF];
                }
                else
                {
                    crc = crcTable[0][four & 0xFF] ^ crcTable[1]
                        [(four >> 8) & 0xFF] ^ crcTable[2][(four >> 16) & 0xFF]
                        ^ crcTable[3][(four >> 24) & 0xFF] ^ crcTable[4]
                        [three & 0xFF] ^ crcTable[5][(three >> 8) & 0xFF] ^ crcTable
                        [6][(three >> 16) & 0xFF] ^ crcTable[7][(three >> 24) & 0xFF]
                        ^ crcTable[8][two & 0xFF] ^ crcTable[9][(two >> 8) & 0xFF]
                        ^ crcTable[10][(two >> 16) & 0xFF] ^ crcTable[11]
                        [(two >> 24) & 0xFF] ^ crcTable[12][one & 0xFF] ^ crcTable
                        [13][(one >> 8) & 0xFF] ^ crcTable[14][(one >> 16) & 0xFF]
                        ^ crcTable[15][(one >> 24) & 0xFF];
                }

                unrolling++;
            }

            length -= bytesAtOnce;
        }

        var srcPtr2 = (byte*) srcPtr;
        // remaining 1 to 63 bytes (standard algorithm)
        while (length != 0)
        {
            crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
            srcPtr2++;
            length--;
        }

        CurrentCRC = ~crc;
    }
}

On my ancient machine, here's the relative throughput I got:
C#:
657 659
684 680
676 677
Timings on a old AMD system circa 2012:
                         Castagnoli PKZip  (in MB/s)
Original                    657      659
Original without branch     684      680
Span<T> without branch      676      677
 

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
@Skydiver , thanks a lot for this, from your benchmark I see that original without branch is faster than the Span<T> variant?
any chance you can share the code for original without branch please?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
Yes, it is faster, but also more complex. Its still uses unsafe pointers. The reason I put in that statistic there was to show that using the Span<T> lets you have safer code, but the performance penalty is not that big.

See pull Request #4. The relevant code is:
C#:
protected unsafe void LocalCrcCompute(uint[][] crcTable, byte[] data, int index,
                                      int length)
{
    if (data == null) throw new ArgumentNullException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;
    var crc = ~CurrentCRC;

    if (BitConverter.IsLittleEndian)
        ComputeLittleEndianBlocks();
    else
        ComputeBigEndianBlocks();

    CurrentCRC = ~crc;

    void ComputeLittleEndianBlocks()
    {
        fixed (byte* dataPtr = data)
        {
            var srcPtr = (uint*)(dataPtr + index);
            while (length >= bytesAtOnce)
            {
                var unrolling = 0;
                while (unrolling < unroll)
                {
                    var one = Converters.ReadPCardinalAsUInt32(srcPtr) ^ crc;
                    srcPtr++;
                    var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;

                    crc = crcTable[0][(four >> 24) & 0xFF] ^ crcTable[1]
                        [(four >> 16) & 0xFF] ^ crcTable[2][(four >> 8) & 0xFF]
                        ^ crcTable[3][four & 0xFF] ^ crcTable[4]
                        [(three >> 24) & 0xFF] ^ crcTable[5][(three >> 16) & 0xFF]
                        ^ crcTable[6][(three >> 8) & 0xFF] ^ crcTable[7]
                        [three & 0xFF] ^ crcTable[8][(two >> 24) & 0xFF] ^ crcTable
                        [9][(two >> 16) & 0xFF] ^ crcTable[10][(two >> 8) & 0xFF]
                        ^ crcTable[11][two & 0xFF] ^ crcTable[12][(one >> 24) & 0xFF]
                        ^ crcTable[13][(one >> 16) & 0xFF] ^ crcTable[14]
                        [(one >> 8) & 0xFF] ^ crcTable[15][one & 0xFF];

                    unrolling++;
                }

                length -= bytesAtOnce;
            }

            var srcPtr2 = (byte*)srcPtr;
            // remaining 1 to 63 bytes (standard algorithm)
            while (length != 0)
            {
                crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
                srcPtr2++;
                length--;
            }
        }
    }


    void ComputeBigEndianBlocks()
    {
        fixed (byte* dataPtr = data)
        {
            var srcPtr = (uint*)(dataPtr + index);
            while (length >= bytesAtOnce)
            {
                var unrolling = 0;
                while (unrolling < unroll)
                {
                    var one = Converters.ReadPCardinalAsUInt32(srcPtr) ^ Bits.ReverseBytesUInt32(crc);
                    srcPtr++;
                    var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;

                    crc = crcTable[0][four & 0xFF] ^ crcTable[1]
                        [(four >> 8) & 0xFF] ^ crcTable[2][(four >> 16) & 0xFF]
                        ^ crcTable[3][(four >> 24) & 0xFF] ^ crcTable[4]
                        [three & 0xFF] ^ crcTable[5][(three >> 8) & 0xFF] ^ crcTable
                        [6][(three >> 16) & 0xFF] ^ crcTable[7][(three >> 24) & 0xFF]
                        ^ crcTable[8][two & 0xFF] ^ crcTable[9][(two >> 8) & 0xFF]
                        ^ crcTable[10][(two >> 16) & 0xFF] ^ crcTable[11]
                        [(two >> 24) & 0xFF] ^ crcTable[12][one & 0xFF] ^ crcTable
                        [13][(one >> 8) & 0xFF] ^ crcTable[14][(one >> 16) & 0xFF]
                        ^ crcTable[15][(one >> 24) & 0xFF];

                    unrolling++;
                }

                length -= bytesAtOnce;
            }

            var srcPtr2 = (byte*)srcPtr;
            // remaining 1 to 63 bytes (standard algorithm)
            while (length != 0)
            {
                crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
                srcPtr2++;
                length--;
            }
        }
    }
}
 

Xor-el

Member
Joined
May 28, 2020
Messages
16
Programming Experience
3-5
@Skydiver, Okay I see, thanks for the pull request, I will merge pull request no 4 since I will favour performance for this case.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,762
Location
Chesapeake, VA
Programming Experience
10+
No problem. It's your library. I guess you'll just deal with any cases when someone figures out how to use your library to cause a buffer overwrite or an elevation of privilege when somebody reports it. Performance is king, that's why we still program in languages like C and C++... Security, smecurity... it's someone else's identity that gets stolen or machine the gets owned...
 
Top Bottom