vhost.exe crashes when trying to make waveout work in C# - c#

I tried to use waveout in C# to play more wav files simultaneously (at least the different ones). (SoundPlayer object only plays one in a time, and I don't want to use DirectSound and MediaPlayer objects or any other advenced technologies for this simple goal.)
I ported a working C++ code to C# and it works (after a research how to marshal and how to call native win32 dll-s and how to allocate and lock unmanaged memory) but it somehow make the vhost.exe crash and I have no clue why it does this. (It does not throw any exception only the standard windows error dialog appears and the program crashes and exit.)
Does anyone have any ideas?
Here is the source of that class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using XiVo.PlayThrough;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Timers;
namespace RTS
{
class WaveObject
{
protected IntPtr wo;
protected byte[] Adat=null;
protected WaveNative.WaveHdr woh;
protected int pgc;
public class NotAWaveformFileException : Exception
{
public NotAWaveformFileException(string str) : base(str) { }
public NotAWaveformFileException() { }
}
public class WaveFileIsCorruptedException : Exception
{
public WaveFileIsCorruptedException(string str) : base(str) { }
public WaveFileIsCorruptedException() { }
}
public class WaveMapperCouldNotBeOpenedException : Exception
{
public WaveMapperCouldNotBeOpenedException(string str) : base(str) { }
public WaveMapperCouldNotBeOpenedException() { }
}
public WaveObject() {}
public WaveObject(string Fn) : this() { Load(Fn); }
public void Load(string Fn)
{
IntPtr mmio;
NativeMMIO.mmckInfo Main, Sub;
WaveFormat wfx;
StringBuilder str=new StringBuilder(Fn);
int r;
mmio = NativeMMIO.mmioOpen(str,IntPtr.Zero,NativeMMIO.MMIO_READ);
if (mmio == IntPtr.Zero)
{
throw new FileNotFoundException(Fn + "not found!");
}
Main.fccType = NativeMMIO.mmioFOURCC('W', 'A', 'V', 'E');
if (NativeMMIO.mmioDescend(mmio, out Main, IntPtr.Zero, NativeMMIO.MMIO_FINDRIFF) != 0)
{
throw new NotAWaveformFileException();
}
Sub.ckid = NativeMMIO.mmioFOURCC('f', 'm', 't', ' ');
if (NativeMMIO.mmioDescend(mmio, out Sub, out Main, NativeMMIO.MMIO_FINDCHUNK) != 0)
{
throw new WaveFileIsCorruptedException("fmt chunk is not found!");
}
byte[] raw = new byte[Sub.cksize+2];
NativeMMIO.mmioRead(mmio, raw, (int)Sub.cksize);
GCHandle conv = GCHandle.Alloc(raw, GCHandleType.Pinned); // mapping a WaveFormat structure from the byte array
wfx = (WaveFormat)Marshal.PtrToStructure(conv.AddrOfPinnedObject(), typeof(WaveFormat));
conv.Free();
Sub.ckid = NativeMMIO.mmioFOURCC('d', 'a', 't', 'a');
if (NativeMMIO.mmioDescend(mmio, out Sub, out Main, NativeMMIO.MMIO_FINDCHUNK) != 0)
{
throw new WaveFileIsCorruptedException("data chunk is not found!");
}
Adat = new byte[Sub.cksize+2];
NativeMMIO.mmioRead(mmio, Adat, (int)Sub.cksize);
NativeMMIO.mmioClose(mmio, 0);
wfx.cbSize = (short)Marshal.SizeOf(wfx);
unchecked // WAVE_MAPPER is 0xFFFFFFFF and it does not let it convert to int otherwise
{
int res = WaveNative.waveOutOpen(out wo, (int)WaveNative.WAVE_MAPPER, wfx, null, 0, (int)WaveNative.CALLBACK_NULL);
if (res != WaveNative.MMSYSERR_NOERROR)
{
throw new WaveMapperCouldNotBeOpenedException();
}
}
woh.lpData = Marshal.AllocHGlobal((int)Sub.cksize); // alloc memory for the buffer
Marshal.Copy(Adat, 0, woh.lpData, (int)Sub.cksize);
woh.dwBufferLength = (int)Sub.cksize;
woh.dwBytesRecorded = 0;
woh.dwUser = IntPtr.Zero;
woh.dwFlags = 0;
woh.dwLoops = 1000000;
woh.reserved = 0;
woh.lpNext = IntPtr.Zero;
r = WaveNative.waveOutPrepareHeader(wo, ref woh, Marshal.SizeOf(woh));
pgc = System.Environment.TickCount;
}
public void Play()
{
if (System.Environment.TickCount - pgc > 50)
{
if (wo == null) throw new Exception("wo somehow became null.");
int res = WaveNative.waveOutReset(wo);
if (res != WaveNative.MMSYSERR_NOERROR) throw new Exception(string.Format("waveOutReset {0}",res));
res=WaveNative.waveOutWrite(wo, ref woh, Marshal.SizeOf(woh));
if ((res != WaveNative.MMSYSERR_NOERROR) && (res!=33)) throw new Exception(string.Format("waveOutWrite {0}",res));
}
}
~WaveObject()
{
Marshal.FreeHGlobal(woh.lpData); // release memory
}
}
}

After a lot of googling I realized that the bug is in the winmm.dll itself. Under some circumstances its helper thread crashes and bring down the host application too.
citation

Related

How do I get a Non-Nullable T in C# to use it in a function?

got an error in this code.
private void Save<T>(string file)
where T : struct, IPixel<T>
{
Image<T> image = Image.LoadPixelData<T>(
_image.Data, _image.Width, _image.Height);
image.Save(file);
}
CS8377 C# The type 'T' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter in the generic type or method
Im using C#7.3, .Net-Framework 4.6 and wpf
this code works with winforms
[EDIT]
Image is from Sixlabors
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.IO;
namespace DDSReader
{
public class LoadImage
{
private readonly Pfim.IImage _image;
public byte[] Data
{
get
{
if (_image != null)
return _image.Data;
else
return new byte[0];
}
}
public LoadImage(string file)
{
_image = Pfim.Pfim.FromFile(file);
Process();
}
public LoadImage(Stream stream)
{
if (stream == null)
throw new Exception("DDSImage ctor: Stream is null");
_image = Pfim.Dds.Create(stream, new Pfim.PfimConfig());
Process();
}
public LoadImage(byte[] data)
{
try
{
if (data == null || data.Length <= 0)
throw new Exception("DDSImage ctor: no data");
_image = Pfim.Dds.Create(data, new Pfim.PfimConfig());
Process();
}
catch { }
}
public void Save(string file)
{
try
{
if (_image.Format == Pfim.ImageFormat.Rgba32)
Save<Bgra32>(file);
else if (_image.Format == Pfim.ImageFormat.Rgb24)
Save<Bgr24>(file);
else
throw new Exception("Unsupported pixel format (" + _image.Format + ")");
}
catch { }
}
private void Process()
{
if (_image == null)
throw new Exception("DDSImage image creation failed");
if (_image.Compressed)
_image.Decompress();
}
private void Save<T>(string file)
where T : struct, IPixel<T>
{
Image<T> image = Image.LoadPixelData()
Image.LoadPixelData<T>(
_image.Data, _image.Width, _image.Height);
image.Save(file);
}
}
}
this Program reads an Image from an other File which is in DDS-Format
Im Using the SixlaborsImagesharpFramework to get an Image
and to read the image the PfimFramework
Try using unmanaged type constraint:
private void Save<T>(string file)
where T : unmanaged, IPixel<T>
It seems that Image<T> comes from SixLabors.ImageSharp, you can check which constraints they are using at their github page.

envdte doesn't fire some Debug events

I'm trying to use envdte to communicate with visual studio debugger. Unfortunately I can't fire events OnEnterBreakMode/OnEnterRunMode/OnEnterDesignMode.
Event OnContextChanged works well, but the others are not fired.
Below Is my code to reproduce the problem C# Console Application (I was trying to simplify it as much as possible)
Most important is class Debugger. I test it in such way:
1. Compile code below
2. Open c++ project in Visual studio
3. Run compiled program from step 1
4. Connect to Visual Studio (type 0 and enter)
5. Start Debugging this c++ project - run debugging, add break points step, step in, step out, f5 ...
Finally the only output I can receive looks like below:
Select Debugger Number:
0 - !VisualStudio.DTE.15.0:13000
0
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
My test code:
using System;
using System.Collections.Generic;
using EnvDTE;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace DebugWatchTest
{
class DebuggerConnector
{
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static List<string> FindDebuggers()
{
List<string> result = new List<string>();
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
string name = null;
IMoniker runningObjectMoniker = moniker[0];
if (runningObjectMoniker != null)
{
try {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{ } // Do nothing, there is something in the ROT that we do not have access to.
}
const string progName = "!VisualStudio.DTE";
if (!string.IsNullOrEmpty(name) && name.StartsWith(progName, StringComparison.Ordinal))
{
result.Add(name);
}
}
}
finally
{
if (enumMonikers != null) {
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null) {
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null) {
Marshal.ReleaseComObject(bindCtx);
}
}
return result;
}
public static DTE Connect(string debuggerName)
{
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
string name = null;
IMoniker runningObjectMoniker = moniker[0];
if (runningObjectMoniker != null)
{
try {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{ } // Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && name.Equals(debuggerName, StringComparison.Ordinal))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
break;
}
}
}
finally
{
if (enumMonikers != null) {
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null) {
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null) {
Marshal.ReleaseComObject(bindCtx);
}
}
if (runningObject is DTE)
{
return runningObject as DTE;
}
return null;
}
}
class Debugger
{
DTE instance;
Events e;
DebuggerEvents de;
public Debugger(DTE d)
{
instance = d;
e = instance.Events;
de = e.DebuggerEvents;
de.OnContextChanged += De_OnContextChanged;
de.OnEnterBreakMode += De_OnEnterBreakMode;
de.OnEnterRunMode += De_OnModeChanged;
de.OnEnterDesignMode += De_OnModeChanged;
}
private void De_OnModeChanged(dbgEventReason Reason) {
Console.WriteLine("Mode Changed");
}
private void De_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
Console.WriteLine("BreakMode");
}
private void De_OnContextChanged(Process NewProcess, EnvDTE.Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
Console.WriteLine("Context Changed");
}
}
class Program
{
static void Main(string[] args)
{
List<string> debuggers = DebuggerConnector.FindDebuggers();
Console.WriteLine("Select Debugger Number:");
for(int i = 0; i < debuggers.Count;++i)
Console.WriteLine($"{i} - {debuggers[i]}");
string number = Console.ReadLine();
string processId = debuggers[int.Parse(number)];
DTE msvc = DebuggerConnector.Connect(processId);
Debugger d = new Debugger(msvc);
while (true)
{ }
}
}
}
Aby hints what is wrong in my code ?

clrobj(<class name>) does not have llvm when passing array of struct to GPU Kernel (ALEA Library)

I am getting the "Fody/Alea.CUDA: clrobj(cGPU) does not have llvm" build error for a code in which I try to pass an array of struct to the NVIDIA Kernel using ALEA library. Here is a simplified version of my code. I removed the output gathering functionality in order to keep the code simple. I just need to be able to send the array of struct to the GPU for the moment.
using Alea.CUDA;
using Alea.CUDA.Utilities;
using Alea.CUDA.IL;
namespace GPUProgramming
{
public class cGPU
{
public int Slice;
public float FloatValue;
}
[AOTCompile(AOTOnly = true)]
public class TestModule : ILGPUModule
{
public TestModule(GPUModuleTarget target) : base(target)
{
}
const int blockSize = 64;
[Kernel]
public void Kernel2(deviceptr<cGPU> Data, int n)
{
var start = blockIdx.x * blockDim.x + threadIdx.x;
int ind = threadIdx.x;
var sharedSlice = __shared__.Array<int>(64);
var sharedFloatValue = __shared__.Array<float>(64);
if (ind < n && start < n)
{
sharedSlice[ind] = Data[start].Slice;
sharedFloatValue[ind] = Data[start].FloatValue;
Intrinsic.__syncthreads();
}
}
public void Test2(deviceptr<cGPU> Data, int n, int NumOfBlocks)
{
var GridDim = new dim3(NumOfBlocks, 1);
var BlockDim = new dim3(64, 1);
try
{
var lp = new LaunchParam(GridDim, BlockDim);
GPULaunch(Kernel2, lp, Data, n);
}
catch (CUDAInterop.CUDAException x)
{
var code = x.Data0;
Console.WriteLine("ErrorCode = {0}", code);
}
}
public void Test2(cGPU[] Data)
{
int NumOfBlocks = Common.divup(Data.Length, blockSize);
using (var d_Slice = GPUWorker.Malloc(Data))
{
try
{
Test_Kernel2(d_Slice.Ptr, Data.Length, NumOfBlocks);
}
catch (CUDAInterop.CUDAException x)
{
var code = x.Data0;
Console.WriteLine("ErrorCode = {0}", x.Data0);
}
}
}
}
}
Your data is class, which is reference type. Try use struct. Reference type doesn't fit Gpu well, since it require of allocating small memory on the heap.

Drag and Drop large virtual files with IStream using VirtualFileDataObject

I am successfully using VirtualFileDataObject code from Delay's blog, but i want to avoid streaming the entire file into memory.
I found this previously answered question on Stack Overflow Drag and Drop large virtual files from c# to Windows Explorer The question was answered by matthieu, by changing the signature of the SetData method.
Here is my problem, after changing the signature of the SetData method, other places that call it are still looking for the old signature.
Here is the original SetData;
public void SetData(short dataFormat, int index, Action<Stream> streamData)
{
_dataObjects.Add(
new DataObject
{
FORMATETC = new FORMATETC
{
cfFormat = dataFormat,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = index,
tymed = TYMED.TYMED_ISTREAM
},
GetData = () =>
{
// Create IStream for data
var ptr = IntPtr.Zero;
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
if (streamData != null)
{
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream))
{
streamData(stream);
}
}
// Return an IntPtr for the IStream
ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
});
}
matthieu suggested to change it to;
public void SetData(short dataFormat, int index, Stream stream)
{
...
var iStream = new StreamWrapper(stream);
...
// Ensure the following line is commented out:
//Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
...
}
After I make these changes the following call will not work; ( and this is where i need help)
How do i fix this call;
foreach (var fileDescriptor in fileDescriptors)
{
**SetData(FILECONTENTS, index, fileDescriptor.StreamContents);**
index++;
}
Basically changing "Action streamData" To "Stream stream" is causing my problems. I am not sure on how to call it after the changes are made.
All this code comes from Delays VirtualFileDataObject. I don't know if i should post it on here or not. But if you follow the link above it will take you to the blog so you can view it.
I am so close, just can't figure this last step out, thanks for taking a look
I've had exactly the same problem. Here is what I did to fix this issue (which as you say has not been fully addressed in the other answer)
1) Modify FileDescriptor's StreamContents property from this:
public Action<Stream> StreamContents { get; set; }
to this:
public Func<Stream> StreamContents { get; set; }
(instead of passing a Stream the client can write, we'll expect a Stream we can read from, which is exactly how Explorer works and what it expects)
2) Modify the SetData method overload from this:
public void SetData(short dataFormat, int index, Action<Stream> streamData)
to this:
public void SetData(short dataFormat, int index, Func<Stream> streamData)
3) change SetData code's GetData lambda to this:
GetData = () =>
{
ManagedIStream istream = null;
if (streamData != null)
{
Stream stream = streamData();
if (stream != null)
{
istream = new ManagedIStream(stream);
}
}
IntPtr ptr = istream != null ? Marshal.GetComInterfaceForObject(istream, typeof(IStream)) : IntPtr.Zero;
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
4) add this ManagedIStream class to the code (you can also delete the IStreamWrapper class completely)
private class ManagedIStream : IStream
{
private Stream _stream;
public ManagedIStream(Stream stream)
{
_stream = stream;
}
public void Clone(out IStream ppstm)
{
throw new NotImplementedException();
}
public void Commit(int grfCommitFlags)
{
throw new NotImplementedException();
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new NotImplementedException();
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
int read = _stream.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero)
{
Marshal.WriteInt32(pcbRead, read);
}
}
public void Revert()
{
throw new NotImplementedException();
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long newPos = _stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
if (plibNewPosition != IntPtr.Zero)
{
Marshal.WriteInt64(plibNewPosition, newPos);
}
}
public void SetSize(long libNewSize)
{
_stream.SetLength(libNewSize);
}
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
{
const int STGTY_STREAM = 2;
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
pstatstg.type = STGTY_STREAM;
pstatstg.cbSize = _stream.Length;
pstatstg.grfMode = 0;
if (_stream.CanRead && _stream.CanWrite)
{
const int STGM_READWRITE = 0x00000002;
pstatstg.grfMode |= STGM_READWRITE;
return;
}
if (_stream.CanRead)
{
const int STGM_READ = 0x00000000;
pstatstg.grfMode |= STGM_READ;
return;
}
if (_stream.CanWrite)
{
const int STGM_WRITE = 0x00000001;
pstatstg.grfMode |= STGM_WRITE;
return;
}
throw new IOException();
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero)
{
Marshal.WriteInt32(pcbWritten, cb);
}
}
}
That's it. Now you can use the code like this (using the same sample as in the original article available here: http://dlaa.me/blog/post/9913083):
new VirtualFileDataObject.FileDescriptor
{
Name = "Alphabet.txt",
Length = 26,
ChangeTimeUtc = DateTime.Now.AddDays(-1),
StreamContents = () =>
{
var contents = Enumerable.Range('a', 26).Select(i => (byte)i).ToArray();
MemoryStream ms = new MemoryStream(contents); // don't dispose/using here, it would be too early
return ms;
}
};

.Net Lists and maximum number of elements

So I am developing an append-only 64-bit-ish List and Dictionary, and I've run into a memory error. I figured I would at some point, but not at 64 MBs. I find that somewhat unexpected, and am curious if someone could explain to me why it's running into an issue at 64 MBs.
My test for my new List class is simply an attempt to create and load 8 GBs worth of bools into the List. I figured they'd suck up only ~1 bit each, so I'd get some good metrics / precision for testing my program.
Here is the output from VS:
- this {OrganicCodeDesigner.DynamicList64Tail<bool>} OrganicCodeDesigner.DynamicList64Tail<bool>
Count 536870912 ulong
- data Count = 536870912 System.Collections.Generic.List<bool>
- base {"Exception of type 'System.OutOfMemoryException' was thrown."} System.SystemException {System.OutOfMemoryException}
- base {"Exception of type 'System.OutOfMemoryException' was thrown."} System.Exception {System.OutOfMemoryException}
+ Data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
HelpLink null string
+ InnerException null System.Exception
Message "Exception of type 'System.OutOfMemoryException' was thrown." string
Source "mscorlib" string
StackTrace " at System.Collections.Generic.Mscorlib_CollectionDebugView`1.get_Items()" string
+ TargetSite {T[] get_Items()} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}
+ Static members
+ Non-Public members
- Raw View
Capacity 536870912 int
Count 536870912 int
- Static members
+ Non-Public members
- Non-Public members
+ _items {bool[536870912]} bool[]
_size 536870912 int
_syncRoot null object
_version 536870912 int
System.Collections.Generic.ICollection<T>.IsReadOnly false bool
System.Collections.ICollection.IsSynchronized false bool
System.Collections.ICollection.SyncRoot {object} object
System.Collections.IList.IsFixedSize false bool
System.Collections.IList.IsReadOnly false bool
item false bool
- Type variables
T bool bool
And here are the classes I am currently working on:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OrganicCodeDesigner
{
public class DynamicList64Tail<T> : iList64<T>
{
private List<T> data;
public DynamicList64Tail()
{
data = new List<T>();
}
public void Add(T item)
{
data.Add(item);
}
public void Clear()
{
data.Clear();
}
public bool Contains(T item)
{
return data.Contains(item);
}
public ulong? IndexOf(T item)
{
if(this.data.Contains(item)) {
return (ulong)data.IndexOf(item);
}
return null;
}
public T this[ulong index]
{
get
{
return data[(int)(index)];
}
set
{
data[(int)(index)] = value;
}
}
public ulong Count
{
get { return (ulong)data.Count; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace OrganicCodeDesigner
{
// #todo: Create IList64, with 64-bit longs in mind.
// #todo: Create BigIntegerList, which may supersede this one.
public class DynamicList64<T> : iList64<T>
{
private List<iList64<T>> data;
private ulong count = 0;
private ulong depth = 0;
public DynamicList64()
{
data = new List<iList64<T>>() { new DynamicList64Tail<T>()};
count = 0;
}
public DynamicList64(ulong depth)
{
this.depth = depth;
if (depth == 0)
{
data = new List<iList64<T>>() { new DynamicList64Tail<T>() };
}
else
{
depth -= 1;
data = new List<iList64<T>>() { new DynamicList64<T>(depth) };
}
}
internal DynamicList64(List<iList64<T>> data, ulong depth)
{
this.data = data;
this.depth = depth;
this.count = Int32.MaxValue;
}
public void Add(T item)
{
if (data.Count >= Int32.MaxValue)
{
//#todo: Do switch operation, whereby this {depth, List l} becomes this {depth + 1, List.Add(List l), count = 1}, and the new object becomes {depth, List l, count = max}
DynamicList64<T> newDynamicList64 = new DynamicList64<T>(this.data, this.depth);
this.data = new List<iList64<T>>() { newDynamicList64 };
this.count = 0;
this.depth += 1;
}
if(data[data.Count-1].Count >= Int32.MaxValue) {
if (depth == 0)
{
data.Add(new DynamicList64Tail<T>());
}
else
{
data.Add(new DynamicList64<T>(depth - 1));
}
}
data[data.Count - 1].Add(item);
count++;
}
public void Clear()
{
data.Clear();
data = new List<iList64<T>>() { new DynamicList64Tail<T>() };
count = 0;
depth = 0;
}
public bool Contains(T item)
{
foreach(iList64<T> l in data) {
if(l.Contains(item)) {
return true;
}
}
return false;
}
public ulong? IndexOf(T item)
{
for (int i = 0; i < data.Count; i++ )
{
if (data[i].Contains(item))
{
return (ulong)(((ulong)i * (ulong)(Int32.MaxValue)) + data[i].IndexOf(item).Value);
}
}
return null;
}
public T this[ulong index]
{
get
{
return data[(int)(index / Int32.MaxValue)][index % Int32.MaxValue];
}
set
{
data[(int)(index / Int32.MaxValue)][index % Int32.MaxValue] = value;
}
}
public ulong Count
{
get { return this.count; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OrganicCodeDesigner
{
public interface iList64<T>
{
void Add(T item);
void Clear();
bool Contains(T item);
ulong? IndexOf(T item);
T this[ulong index] { get; set;}
ulong Count { get; }
}
}
And the test program's code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OrganicCodeDesigner;
namespace OrganicCodeDesignerListDictionaryTest
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void Button_TestList_Click(object sender, EventArgs e)
{
DynamicList64<bool> newList = new DynamicList64<bool>();
newList.Add(true);
newList.Add(false);
bool b = true;
for (ulong i = 0; i < 68719476736; i++)
{
b = !b;
newList.Add(b);
//if(i%4096==0) {
//TextBox_Output.Text += "List now contains " + i + "\r";
//}
}
TextBox_Output.Text += "Successfully added all the bits.\r";
}
private void Button_TestDictionary_Click(object sender, EventArgs e)
{
}
}
}
Perhaps you can spot the error?
Perhaps you can spot the error?
I think the error is here:
I figured they'd suck up only ~1 bit each, so I'd get some good metrics / precision for testing my program.
A bool takes one byte, not one bit - so you've drastically underestimated the size of your list. You're actually running into an error with 512MB of bools. As Reed Copsey is editing a little faster than me - I suspect the list is trying to increase its size by allocating an array 2x it's current size [i.e. a 1GB array] and that this is running into some .net limitations.
This is probably a good time to start implementing your splitting logic.
There are limits to the size of an array in .NET. Even if you are running on 64bit platforms, and set gcAllowVeryLargeObjects (in .NET 4.5), you are still limited to 2,146,435,071 items max in a single dimension of the array.
(In pre-4.5, you are limited by 2gb for a single object, no matter how many entries it contains.)
That being said, a bool is represented by one byte, not one bit, so this will be quite a bit larger than you're expecting. That being said, you still only have 536,870,912 in your list when this fails, so theoretically, on a 64bit system with enough memory, the next allocation for growing the list should still be within the limits. However, this requires the program to succesfully allocate a single, contiguous chunk of memory large enough for the requested data (which will be 2x the size of the last chunk).

Categories