Showing posts with label Let Me Understand C#. Show all posts
Showing posts with label Let Me Understand C#. Show all posts

2020-12-27

3: Windows Clipboard with C#: in Multi-Formats

<The previous article in this series | The table of contents of this series |

.NET Framework is used, so applicable to Visual Basic.NET too. Many formats are supported.

Topics


About: C#

The table of contents of this article


Starting Context


  • The reader has a basic knowledge on C#.

Target Context


  • The reader will know how to get and set a composite of format-specific data from and to the Windows clipboard, in C#, or in Viusual Basic.NET.

Orientation


Many, but not all the, formats are supported by this technique.

All the formats are supported by a technique in C++ for Linux or Microsoft Windows, as will be introduced in a future article of another series (for Windows).


Main Body


1: Aim: to Implement Multiple Copy Buffers for a Word Processor


Hypothesizer 7
As I use Vim as a text editor, one of the benefits is that it has multiple copy buffers: I can copy a piece of text to the buffer 'a', another piece of text to the buffer 'b', etc.. That is huge help.

As I use LibreOffice Writer as a word processor, I want that functionality in it.

In fact, I tried to implement the functionality in Python, and it works partially, but not ideally.

The 2 dissatisfactions about the technique are 1) the possible datum formats have to be known a priori and 2) the supported datum formats are limited.

.NET Framework has a clipboard handling functionality, and I hoped that the functionality had the capability of handling the Windows clipboard in any possible way, because .NET Framework is Microsoft specific.

Well, supposing that the .NET Framework functionality is satisfactory, how can I call the C# code from the Python macro (as any macro cannot be written in C#)? . . . I will create an HTTP server in C#, which the Python macro will access.


2: Some Characteristics of the .NET Framework Clipboard Functionality


Hypothesizer 7
The .NET Framework clipboard functionality is characteristic in some points, as is seen in the API document.

While according to the Win32 API, each datum format is identified by a number, such numbers cannot be seen in .NET Framework; in .NET Framework, each datum format is identified by the name.

There is no need to explicitly open or close the clipboard.

What to be done for getting the clipboard data are basically straightforward: call 'System.Windows.Forms.Clipboard.GetDataObject ()' to get the multi-formats composite; call 'System.Windows.IDataObject.GetFormats (Boolean)' to get the format names from the multi-formats composite; call 'System.Windows.IDataObject.GetData (String, Boolean)' to get the datum of each format.

An issue is that the datum of each of many formats is gotten as a 'System.IO.MemoryStream' instance. . . . As stashing the instance itself seems not to work well, I read the bytes array out and save it.

Another issue is that the datum of each of some formats (like 'EnhancedMetafile' and 'MetaFilePict') cannot be gotten ('System.Windows.IDataObject.GetData (String, Boolean)' returns null). . . . Why? Well, in Microsoft Windows, it is not that the datum of any format can be just gotten in a uniform way as a bytes array. The datum of a format have to be gotten in a certain way, but .NET Framework has not made the effort of supporting all the possible formats. What can I do about it? Well, nothing, as far as I know (unless I use the Win32 API).

What to be done for setting the stashed data to the clipboard are straightforward: create an instance of 'System.Windows.DataObject' and call the 'System.Windows.IDataObject.SetData (String, Object, Boolean)' method of it per format to prepare a multi-formats composite; call 'System.Windows.Forms.Clipboard.SetDataObject (Object, Boolean)' to set the multi-formats composite to the clipboard.

The bytes arrays seem to be able to put in as they are.


3: My Code


Hypothesizer 7
As my aim is to store a history of clipboard data, I have created some classes as the storage, like these.

theBiasPlanet/coreUtilities/clipboardHandling/ClipboardFormatSpecificDatum.cs

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace clipboardHandling {
			using System;
			
			public class ClipboardFormatSpecificDatum {
				private String i_formatName;
				private	Object i_datum;
				
				public ClipboardFormatSpecificDatum () {
				}
				
				public ClipboardFormatSpecificDatum (String a_formatName, Object a_datum) {
					i_formatName = a_formatName;
					i_datum = a_datum;
				}
				
				~ClipboardFormatSpecificDatum () {
				}
				
				public String getFormatName () {
					return i_formatName;
				}
				
				public Object getDatum () {
					return i_datum;
				}
			}
		}
	}
}

theBiasPlanet/coreUtilities/clipboardHandling/ClipboardFormatSpecificDataComposite.cs

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace clipboardHandling {
			using System;
			using System.Collections.Generic;
			using theBiasPlanet.coreUtilities.collections;
			
			public class ClipboardFormatSpecificDataComposite {
				private NavigableLinkedHashMap <String, ClipboardFormatSpecificDatum> i_formatNameToDatumMap = new NavigableLinkedHashMap <String, ClipboardFormatSpecificDatum> ();
				
				public ClipboardFormatSpecificDataComposite () {
				}
				
				~ClipboardFormatSpecificDataComposite () {
				}
				
				public Boolean addFormatSpecificDatum (ClipboardFormatSpecificDatum a_formatSpecificDatum) {
					try {
						i_formatNameToDatumMap [a_formatSpecificDatum.getFormatName ()] = a_formatSpecificDatum;
					}
					catch (KeyNotFoundException) {
						i_formatNameToDatumMap.Add (a_formatSpecificDatum.getFormatName (), a_formatSpecificDatum);
					}
					return true;
				}
				
				public List <String> getFormatNames () {
					List <String> l_formatNames = new List <String> ();
					
					foreach (KeyValuePair <String, ClipboardFormatSpecificDatum> l_formatNameToDatumMapEntry in i_formatNameToDatumMap) {
						l_formatNames.Add (l_formatNameToDatumMapEntry.Key);
					}
					return l_formatNames;
				}
				
				public ClipboardFormatSpecificDatum getFormatSpecificDatum (String a_formatName) {
					return i_formatNameToDatumMap [a_formatName];
				}
			}
		}
	}
}

theBiasPlanet/coreUtilities/clipboardHandling/ClipboardFormatSpecificDataCompositesHistory.cs

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace clipboardHandling {
			using System;
			using System.Collections.Generic;
			using theBiasPlanet.coreUtilities.collections;
			
			public class ClipboardFormatSpecificDataCompositesHistory {
				private NavigableLinkedHashMap <String, ClipboardFormatSpecificDataComposite> i_dataCompositeKeyToDataCompositeMap = new NavigableLinkedHashMap <String, ClipboardFormatSpecificDataComposite> ();
				
				public ClipboardFormatSpecificDataCompositesHistory () {
				}
				
				~ClipboardFormatSpecificDataCompositesHistory () {
				}
				
				public Boolean addDataComposite (String a_dataCompositeKey, ClipboardFormatSpecificDataComposite a_dataComposite) {
					try {
						i_dataCompositeKeyToDataCompositeMap [a_dataCompositeKey] = a_dataComposite;
					}
					catch (KeyNotFoundException) {
						i_dataCompositeKeyToDataCompositeMap.Add (a_dataCompositeKey, a_dataComposite);
					}
					return true;
				}
				
				public Boolean removeDataComposite (String a_dataCompositeKey) {
					i_dataCompositeKeyToDataCompositeMap.Remove (a_dataCompositeKey);
					return true;
				}
				
				public ClipboardFormatSpecificDataComposite getDataComposite (String a_dataCompositeKey) {
					return i_dataCompositeKeyToDataCompositeMap [a_dataCompositeKey];
				}
			}
		}
	}
}

The clipboard handling class is this.

theBiasPlanet/coreUtilities/clipboardHandling/MicrosoftWindowsClipboard.cs

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace clipboardHandling {
			using System;
			using System.IO;
			using System.Windows;
			using System.Windows.Forms;
			using theBiasPlanet.coreUtilities.constantsGroups;
			
			public class MicrosoftWindowsClipboard {
				public static Boolean clearClipboard () {
					Clipboard.Clear ();
					return true;
				}
				
				public static ClipboardFormatSpecificDataComposite  getFormatSpecificDataComposite () {
					IDataObject l_dataObject = Clipboard.GetDataObject ();
					String [] l_datumFormatNames = l_dataObject.GetFormats (false);
					ClipboardFormatSpecificDataComposite l_formatSpecificDataComposite = new ClipboardFormatSpecificDataComposite ();
					foreach (String l_datumFormatName in l_datumFormatNames) {
						Object l_copiedFormatSpecificDatum = l_dataObject.GetData (l_datumFormatName, false);
						if (typeof (MemoryStream).IsInstanceOfType (l_copiedFormatSpecificDatum)) {
							MemoryStream l_copiedFormatSpecificDatumMemoryStream = (MemoryStream) l_copiedFormatSpecificDatum;
							Int64 l_copiedFormatSpecificDatumSizeMemoryStream = l_copiedFormatSpecificDatumMemoryStream.Length;
							byte [] l_copiedFormatSpecificDatumForMemoryStream = new byte [l_copiedFormatSpecificDatumSizeMemoryStream];
							for (int l_byteIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber; l_byteIndex < l_copiedFormatSpecificDatumSizeMemoryStream; l_byteIndex ++) {
								l_copiedFormatSpecificDatumForMemoryStream [l_byteIndex] = Convert.ToByte (l_copiedFormatSpecificDatumMemoryStream.ReadByte ());
							}
							l_copiedFormatSpecificDatum = l_copiedFormatSpecificDatumForMemoryStream;
						}
						l_formatSpecificDataComposite.addFormatSpecificDatum (new ClipboardFormatSpecificDatum (l_datumFormatName, l_copiedFormatSpecificDatum));
					}
					return l_formatSpecificDataComposite;
				}
				
				public static Boolean setFormatSpecificDataComposite (ClipboardFormatSpecificDataComposite a_formatSpecificDataComposite) {
					IDataObject l_dataObject = new DataObject ();
					foreach (String l_datumFormatName  in a_formatSpecificDataComposite.getFormatNames ()) {
						ClipboardFormatSpecificDatum l_formatSpecificDatum = a_formatSpecificDataComposite.getFormatSpecificDatum (l_datumFormatName);
						Object l_copiedFormatSpecificDatum = l_formatSpecificDatum.getDatum ();
						l_dataObject.SetData (l_datumFormatName, true, l_copiedFormatSpecificDatum);
					}
					Clipboard.SetDataObject (l_dataObject, true);
					return true;
				}
			}
		}
	}
}


4: The Limitations


Hypothesizer 7
In addition to the limitation that the datum of each of some formats like 'EnhancedMetafile' cannot be gotten, in fact, not all formats can be even listed in this technique.

For example, the 'DataObject', 'Ole Private Data', 'Locale', and 'OEMText' formats are not listed by the 'System.Windows.IDataObject.GetFormats (Boolean)' method.


5: Testing


Hypothesizer 7
My test code is this.

@ Source Code
namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace clipboardHandlingTest1 {
			using System;
			using System.Collections.Generic;
			using theBiasPlanet.coreUtilities.clipboardHandling;
			
			public class Test1Test {
				public static void main (String [] a_argumentsArray) {
					test ();
				}
				
				private static void test () {
					ClipboardFormatSpecificDataCompositesHistory l_clipbardFormatSpecificDataCompositesHistory = new ClipboardFormatSpecificDataCompositesHistory ();
					while (true) {
						Console.WriteLine ("### Input 'S' (Set), 'G' (Get), 'D (Display)', or 'Q' (Quit):");
						Console.Out.Flush ();
						String l_userInput;
						l_userInput = Console.ReadLine ();
						String l_clipboardFormatSpecificDataCompositeKey;
						if (l_userInput == "S" || l_userInput == "G" || l_userInput == "D") {
							Console.WriteLine ("### Input the key:");
							Console.Out.Flush ();
							l_clipboardFormatSpecificDataCompositeKey = Console.ReadLine ();
							if (l_userInput == "S") {
								MicrosoftWindowsClipboard.clearClipboard ();
								MicrosoftWindowsClipboard.setFormatSpecificDataComposite (l_clipbardFormatSpecificDataCompositesHistory.getDataComposite (l_clipboardFormatSpecificDataCompositeKey));
							}
							else if (l_userInput == "G") {
								l_clipbardFormatSpecificDataCompositesHistory.addDataComposite (l_clipboardFormatSpecificDataCompositeKey, MicrosoftWindowsClipboard.getFormatSpecificDataComposite ());
							}
							else if (l_userInput == "D") {
								ClipboardFormatSpecificDataComposite l_clipboardFormatSpecificDataComposite = l_clipbardFormatSpecificDataCompositesHistory.getDataComposite (l_clipboardFormatSpecificDataCompositeKey);
								List <String> l_clipboardDatumFormatNames = l_clipboardFormatSpecificDataComposite.getFormatNames ();
								foreach (String l_clipboardDatumFormatName in l_clipboardDatumFormatNames) {
									ClipboardFormatSpecificDatum l_clipboardFormatSpecificDatum = l_clipboardFormatSpecificDataComposite.getFormatSpecificDatum (l_clipboardDatumFormatName);
									Console.WriteLine (String.Format ("### clipboard datum format name: {0:s}", l_clipboardDatumFormatName));
									Console.Out.Flush ();
								}
							}
						}
						else {
							break;
						}
					}
				}
			}
		}
	}
}

Note that the 'Main' (not that "main (String [] a_argumentsArray)") method (not shown above) has to be annotated with '[STAThread]'.

After I have copied a text piece on a 'cmd' terminal, I get an output like this for the 'G' -> 'A' -> 'D' -> 'A' inputs.

@Output
### clipboard datum format name: UnicodeText
### clipboard datum format name: Locale
### clipboard datum format name: Text
### clipboard datum format name: OEMText

After I have copied a text piece on a LibreOffice Writer instance, I get an output like this for the 'G' -> 'A' -> 'D' -> 'A' inputs.

@Output
### clipboard datum format name: Star Embed Source (XML)
### clipboard datum format name: Rich Text Format
### clipboard datum format name: Richtext Format
### clipboard datum format name: HTML (HyperText Markup Language)
### clipboard datum format name: HTML Format
### clipboard datum format name: UnicodeText
### clipboard datum format name: Text
### clipboard datum format name: Link
### clipboard datum format name: Star Object Descriptor (XML)


References


<The previous article in this series | The table of contents of this series |

2020-11-01

2: A C# Objects Pipe, Which Conveys Objects Serially

<The previous article in this series | The table of contents of this series | The next article in this series>

Serially because otherwise, the memory would be used up. A for-objects C# equivalent to 'java.io.PipedWriter' + 'java.io.PipedReader'.

Topics


About: C#

The table of contents of this article


Starting Context


  • The reader has a basic knowledge on C#.

Target Context


  • The reader will know an objects pipe.

Orientation


A C# string pipe will be introduced in the next article.

A Java objects pipe has been introduced in an article of another series.

A C++ objects pipe has been introduced in an article of another series.

A Python objects pipe has been introduced in an article of another series.


Main Body


1: The Motivation and the Plan Are the Same for an Already Introduced Java Objects Pipe


Hypothesizer 7
In fact, an article of another series has already introduced a Java objects pipe.

I refrain from repeating the motivation and the plan that are described there.


2: The Code and Its Explanation


Hypothesizer 7
This is the code of my objects pipe class and its related classes.

'theBiasPlanet/coreUtilities/pipes/ObjectsPipe.cs'

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace pipes {
			using System;
			using System.Collections.Generic;
			using System.Runtime.CompilerServices;
			using System.Threading;
			using theBiasPlanet.coreUtilities.constantsGroups;
			using theBiasPlanet.coreUtilities.inputsHandling;
			using theBiasPlanet.coreUtilities.messagingHandling;
			using theBiasPlanet.coreUtilities.timersHandling;
			
			public class ObjectsPipe <T> {
				protected Object [] i_objects;
				protected Int32 i_bufferSize = 0;
				// No data: i_dataStartIndex == GeneralConstantsConstantsGroup.c_iterationStartNumber && i_dataUntilIndex == GeneralConstantsConstantsGroup.c_iterationStartNumber
				protected Int32 i_dataStartIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
				protected Int32 i_dataUntilIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
				protected Boolean i_isFinishedWriting = false;
				protected Boolean i_isFinishedReading = false;
				protected Boolean i_notificationIsDelayed = false;
				
				public ObjectsPipe (Int32 a_bufferSize, Boolean a_notificationIsDelayed) {
					i_bufferSize = a_bufferSize;
					i_objects = new Object [i_bufferSize];
					i_notificationIsDelayed = a_notificationIsDelayed;
				}
				
				~ObjectsPipe () {
				}
				
				protected Boolean isEmptyWithoutLocking () {
					return i_dataStartIndex == GeneralConstantsConstantsGroup.c_iterationStartNumber && i_dataUntilIndex == GeneralConstantsConstantsGroup.c_iterationStartNumber;
				}
				
				protected Boolean isFullWithoutLocking () {
					return (i_dataStartIndex == GeneralConstantsConstantsGroup.c_iterationStartNumber && i_dataUntilIndex == i_bufferSize) || (i_dataStartIndex != GeneralConstantsConstantsGroup.c_iterationStartNumber && i_dataStartIndex == i_dataUntilIndex);
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				protected void writeWithoutLocking (T a_object, Int32 a_timeOutPeriodInMilliseconds = -1) {
					if (i_isFinishedReading) {
						throw new NoMoreNeedsException ("");
					}
					if (i_isFinishedWriting) {
						i_isFinishedWriting = false;
					}
					while (true) {
						if (isFullWithoutLocking ()) {
							try {
								if (a_timeOutPeriodInMilliseconds == -1) {
									Monitor.Wait (this);
								}
								else if (a_timeOutPeriodInMilliseconds == 0) {
								}
								else {
									Monitor.Wait (this, a_timeOutPeriodInMilliseconds);
								}
							}
							catch (Exception l_exception) {
								Publisher.logErrorInformation (l_exception);
							}
						}
						// Checked again because the status may have changed while this thread was waiting.
						if (i_isFinishedReading) {
							throw new NoMoreNeedsException ("");
						}
						if (!isFullWithoutLocking ()) {
							Boolean l_wasEmpty = isEmptyWithoutLocking ();
							if (i_dataUntilIndex == i_bufferSize) {
								i_objects [GeneralConstantsConstantsGroup.c_iterationStartNumber] = a_object;
								i_dataUntilIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber + 1;
							}
							else {
								i_objects [i_dataUntilIndex] = a_object;
								i_dataUntilIndex ++;
							}
							if ( (!i_notificationIsDelayed && l_wasEmpty) || (i_notificationIsDelayed && isFullWithoutLocking ())) {
								Monitor.PulseAll (this);
							}
							return;
						}
						else {
							if (a_timeOutPeriodInMilliseconds != -1) {
								throw new TimeOutException ("");
							}
						}
					}
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				protected T readWithoutLocking (Int32 a_timeOutPeriodInMilliseconds = -1) {
					T l_readObject;
					if (i_isFinishedReading) {
						i_isFinishedReading = false;
					}
					while (true) {
						if (isEmptyWithoutLocking ()) {
							if (!i_isFinishedWriting) {
								try {
									if (a_timeOutPeriodInMilliseconds == -1) {
										Monitor.Wait (this);
									}
									else if (a_timeOutPeriodInMilliseconds == 0) {
									}
									else {
										Monitor.Wait (this, a_timeOutPeriodInMilliseconds);
									}
								}
								catch (Exception l_exception) {
									Publisher.logErrorInformation (l_exception);
								}
							}
							else {
								throw new NoMoreDataException ("");
							}
						}
						// Checked again because the status may have changed while this thread was waiting.
						if (!isEmptyWithoutLocking ()) {
							Boolean l_wasFull = isFullWithoutLocking ();
							l_readObject = (T) i_objects [i_dataStartIndex];
							i_objects [i_dataStartIndex] = null;
							i_dataStartIndex ++;
							if (i_dataStartIndex == i_dataUntilIndex) {
								i_dataStartIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
								i_dataUntilIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
							}
							else {
								if (i_dataStartIndex == i_bufferSize) {
									i_dataStartIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
								}
							}
							if ( (!i_notificationIsDelayed && l_wasFull) || (i_notificationIsDelayed && isEmptyWithoutLocking ())) {
								Monitor.PulseAll (this);
							}
							return l_readObject;
						}
						else {
							if (i_isFinishedWriting) {
								throw new NoMoreDataException ("");
							}
							if (a_timeOutPeriodInMilliseconds != -1) {
								throw new TimeOutException ("");
							}
						}
					}
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public Boolean isEmpty () {
					return isEmptyWithoutLocking ();
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public Boolean isFull () {
					return isFullWithoutLocking ();
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				[MethodImpl (MethodImplOptions.Synchronized)]
				public void write (T a_object, Int32 a_timeOutPeriodInMilliseconds = -1) {
					writeWithoutLocking (a_object, a_timeOutPeriodInMilliseconds);
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				[MethodImpl (MethodImplOptions.Synchronized)]
				public Int32 write (T [] a_objects, Int32 a_offset, Int32 a_length, Int32 a_timeOutPeriodInMilliseconds = -1) {
					Int32 l_writtenLength = 0;
					for (l_writtenLength = 0; l_writtenLength < a_length; l_writtenLength ++) {
						try {
							if (l_writtenLength == 0 || ! (isFullWithoutLocking ())) {
								writeWithoutLocking (a_objects [a_offset + l_writtenLength], a_timeOutPeriodInMilliseconds);
							}
						}
						catch (NoMoreNeedsException l_exception) {
							if (l_writtenLength == 0) {
								throw l_exception;
							}
							else {
								break;
							}
						}
					}
					return l_writtenLength;
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				[MethodImpl (MethodImplOptions.Synchronized)]
				public T read (Int32 a_timeOutPeriodInMilliseconds = -1) {
					return readWithoutLocking (a_timeOutPeriodInMilliseconds);
				}
				
				// a_timeOutPeriodInMilliseconds: -1 -> waits indefinitely, 0 -> not wait
				[MethodImpl (MethodImplOptions.Synchronized)]
				public Int32 read (T [] a_objects, Int32 a_offset, Int32 a_length, Int32 a_timeOutPeriodInMilliseconds = -1) {
					Int32 l_readLength = 0;
					for (; l_readLength < a_length; l_readLength ++) {
						if ( (l_readLength == 0) || !isEmptyWithoutLocking ()) {
							a_objects [a_offset + l_readLength] = readWithoutLocking (a_timeOutPeriodInMilliseconds);
						}
						else {
							break;
						}
					}
					return l_readLength;
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public List <T> readWholeData () {
					List <T> l_objectsList = new List <T> ();
					while (true) {
						try {
							l_objectsList.Add (readWithoutLocking ());
						}
						catch (NoMoreDataException) {
							break;
						}
					}
					return l_objectsList;
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public void finishWriting () {
					i_isFinishedWriting = true;
					Monitor.PulseAll (this);
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public void finishReading () {
					i_isFinishedReading = true;
					Monitor.PulseAll (this);
				}
				
				[MethodImpl (MethodImplOptions.Synchronized)]
				public void reset () {
					i_isFinishedWriting = false;
					i_isFinishedReading = false;
					i_dataStartIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
					i_dataUntilIndex = GeneralConstantsConstantsGroup.c_iterationStartNumber;
				}
			}
		}
	}
}

'theBiasPlanet/coreUtilities/inputsHandling/NoMoreDataException.cs'

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace inputsHandling {
			using System;
			
			public class NoMoreDataException : Exception {
				public NoMoreDataException (String a_message) : base (a_message) {
				}
			}
		}
	}
}

'theBiasPlanet/coreUtilities/inputsHandling/NoMoreNeedsException.cs'

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace inputsHandling {
			using System;
			
			public class NoMoreNeedsException : Exception {
				public NoMoreNeedsException (String a_message) : base (a_message) {
				}
			}
		}
	}
}

'theBiasPlanet/coreUtilities/timersHandling/TimeOutException.cs'

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilities {
		namespace timersHandling {
			using System;
			
			public class TimeOutException : Exception {
				public TimeOutException (String a_message) : base (a_message) {
				}
			}
		}
	}
}

'theBiasPlanet/coreUtilities/messagingHandling/Publisher.cs' is totally omitted because the used method just writes logs.

The explanation of the code is almost the same for the Java version, but I will do the explanation including the duplication.

The constructor takes the buffer size and the algorithm choice for awaking the readers.

'i_objects' is the buffer.

'i_dataStartIndex' and 'i_dataUntilIndex' are the start index and the until (without including) index of the valued area; when the buffer is empty, they are '0' and '0', respectively.

All the public method templates except the constructor are synchronized on the 'this' object.

The 'readWithoutLocking (Int32 a_timeOutPeriodInMilliseconds = -1)' method has the loop on the basis that there may be some multiple readers: a reader that had been waiting on the empty buffer may have been awaken to find out that the written objects have been already snatched by the other readers, being required to wait once more.

Likewise, the 'writeWithoutLocking (T a_object, Int32 a_timeOutPeriodInMilliseconds = -1)' method has the loop on the basis that there may be some multiple writers.

The behavior of the 'writeWithoutLocking (T a_object, Int32 a_timeOutPeriodInMilliseconds = -1)' method when the pipe has been declared finished reading is somehow different from the behavior of the 'readWithoutLocking (Int32 a_timeOutPeriodInMilliseconds = -1)' method when the pipe has been declared finished writing, because the writer will not need to write any more at all, while the reader will want to read up the objects already stored in the buffer.

Be aware that I have not intensively tested the code, yet, although I have used it in some simple cases, one of which is shown in the next section.


3: An Example Usage and an Execution Result


Hypothesizer 7
This is a sample program that uses the objects pipe.

@C# Source Code
namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace pipesTest1 {
			using System;
			using System.Threading;
			using theBiasPlanet.coreUtilities.inputsHandling;
			using theBiasPlanet.coreUtilities.pipes;
			
			public class Test1Test {
				public static void main (String [] a_argumentsArray) {
					test2 ();
				}
				
				public static void prepareIntegers (ObjectsPipe <Int32> a_writer) {
					for (int l_iterationIndex = 0; l_iterationIndex < 512; l_iterationIndex ++) {
						try {
							a_writer.write (l_iterationIndex);
						}
						catch (NoMoreNeedsException) {
							break;
						}
						Console.Out.WriteLine (String.Format ("### written: {0:d}", l_iterationIndex));
						Console.Out.Flush ();
					}
				}
				
				private static void processIntegers (ObjectsPipe <Int32> a_reader) {
					Int32 l_integer = -1;
					int l_numberOfMultipleOf10s = 0;
					while (true) {
						try {
							l_integer = a_reader.read ();
						}
						catch (NoMoreDataException) {
							break;
						}
						Console.Out.WriteLine (String.Format ("### read: {0:d}", l_integer));
						Console.Out.Flush ();
						if (l_integer % 10 == 0) {
							l_numberOfMultipleOf10s ++;
							Console.Out.WriteLine (String.Format ("### a multiple of 10s is found."));
							Console.Out.Flush ();
						}
					}
					Console.Out.WriteLine (String.Format ("### the number of multiple of 10s is {0:d}.", l_numberOfMultipleOf10s));
					Console.Out.Flush ();
				}
				
				private static void test2 () {
					ObjectsPipe <Int32> l_integersPipe = new ObjectsPipe <Int32> (16, true);
					//ObjectsPipe <Int32> l_integersPipe = new ObjectsPipe <Int32> (16, false);
					Thread l_subThread = new Thread (() => {
						try {
							prepareIntegers (l_integersPipe);
						}
						catch (Exception l_exception) {
							Console.Out.WriteLine (l_exception.ToString ());
						}
						finally {
							try {
								l_integersPipe.finishWriting ();
							}
							catch (Exception l_exception) {
								Console.Out.WriteLine (l_exception.ToString ());
							}
						}
					});
					l_subThread.Start ();
					processIntegers (l_integersPipe);
					l_subThread.Join ();
				}
			}
		}
	}
}

This is an output, in my single-core single-CPU Linux computer (Mono is used) (which should be influencing the output).

@Output
### written: 0
### written: 1
### written: 2
### written: 3
### written: 4
### written: 5
### written: 6
### written: 7
### written: 8
### written: 9
### written: 10
### written: 11
### written: 12
### written: 13
### written: 14
### read: 0
### a multiple of 10s is found.
### read: 1
### read: 2
### read: 3
### read: 4
### read: 5
### read: 6
### read: 7
### read: 8
### read: 9
### read: 10
### a multiple of 10s is found.
### read: 11
### read: 12
### read: 13
### read: 14
### read: 15
### written: 15
~
### written: 496
### written: 497
### written: 498
### written: 499
### written: 500
### written: 501
### written: 502
### written: 503
### written: 504
### written: 505
### written: 506
### written: 507
### written: 508
### written: 509
### written: 510
### written: 511
### read: 496
### read: 497
### read: 498
### read: 499
### read: 500
### a multiple of 10s is found.
### read: 501
### read: 502
### read: 503
### read: 504
### read: 505
### read: 506
### read: 507
### read: 508
### read: 509
### read: 510
### a multiple of 10s is found.
### read: 511
### the number of multiple of 10s is 52.

. . . Well, what happened is that the reader acted first and was made waiting because the pipe was empty; the writer wrote to the full and awoke the reader; the reader began to read; and so on, I guess. . . . Note that '15' should have been written before the reader was awoken although "### written: 15" was delayed shown: immediately after the 'write' method was finished, the control switched to the reading thread, with the messaging delayed until the control was returned to the writing thread.

When the notification timing mode is set 'false', this is the output.

@Output
### read: 0
### a multiple of 10s is found.
### written: 0
### read: 1
### written: 1
### read: 2
### written: 2
### read: 3
### written: 3
### read: 4
### written: 4
### read: 5
### written: 5
### read: 6
### written: 6
### read: 7
### written: 7
### read: 8
### written: 8
### read: 9
### written: 9
### read: 10
### a multiple of 10s is found.
### written: 10
### written: 11
### written: 12
### written: 13
### written: 14
### written: 15
### written: 16
### written: 17
### written: 18
### written: 19
### written: 20
### written: 21
### written: 22
### written: 23
### written: 24
### written: 25
### written: 26
### read: 11
### read: 12
### read: 13
### read: 14
### read: 15
~
### written: 496
### written: 497
### written: 498
### written: 499
### written: 500
### written: 501
### written: 502
### written: 503
### written: 504
### written: 505
### written: 506
### written: 507
### written: 508
### written: 509
### written: 510
### written: 511
### read: 496
### read: 497
### read: 498
### read: 499
### read: 500
### a multiple of 10s is found.
### read: 501
### read: 502
### read: 503
### read: 504
### read: 505
### read: 506
### read: 507
### read: 508
### read: 509
### read: 510
### a multiple of 10s is found.
### read: 511
### the number of multiple of 10s is 52.

. . . The reader acted first and was made waiting because the pipe was empty; the writer wrote '0' and awoke the reader; the reader began to read; and so on, I guess. . . . Again, "### written: 0" was delayed shown.

In those executions, the clear difference had appeared only for a little while at the beginning.

As I have measured the times taken (with the costly messagings eliminated) in the 2 modes (5 times each mode), the times were {'176,234,000', '93,412,000', '107,652,000', '147,381,000', and '130,540,000'} and {'99,691,000', '260,851,000', '216,234,000', '147,965,000', and '208,565,000'}, respectively, in nanoseconds (obviously, there is no such precision, really). Due to the unstability of the times, I do not particularly draw any conclusion.


References


<The previous article in this series | The table of contents of this series | The next article in this series>