Using NotesStream to convert a string to a byte array

I'm implementing the Blowfish algorithm in LotusScript.  To do so, I need to convert strings into byte arrays.

There are several ways to do this.  One of them involves using NotesStream.  NotesStream.WriteText loads the steam from a String.  NotesStream.Read returns an array of Bytes.  So far so good (and very fast)!

The only problem I have it that NotesStream.Read returns an array that is twice as long as the input string, and every other element in the array is set to zero (0).  I think this is caused by the width of a character in a LotusScript string being 2-bytes, but I want to understand this better.  I can workaround the problem by removing the odd elements, but I would rather not have to do this since this limits the size of the string that can be converted.

Below is a test agent that illustrates this problem.  The test.txt file contains "12345" with no quotes.  Both streams use the "Unicode" character set.
The image below is from the debugger at the Stop line.  Notice that stream1's size is 10 bytes and stream2's size is 5 bytes.  Also notice the values stored in the resulting bytes arrays.

As I mentioned above, I have a workaround that removes the extra elements, but since the return array is double the size I need, this limits the size of the input string to 16,383 rather than 32,767 (without splitting the string into chunks).

Does anyone know how to make NotesStream work the way I want?
Sub Initialize
	
	Dim sess As New NotesSession
	Dim stream1 As NotesStream
	Dim stream2 As NotesStream
	Dim bytes1 As Variant
	Dim bytes2 As Variant
	
	Set stream1 = sess.CreateStream
	Call stream1.WriteText("12345")
	stream1.Position = 0
	bytes1 = stream1.Read
	
	Set stream2 = sess.CreateStream
	Call stream2.Open("c:\temp\test.txt", "Unicode")
	stream1.Position = 0
	bytes2 = stream2.Read
	Stop
	
End Sub

Open in new window

streams.GIF
LVL 22
Bill-HansonAsked:
Who is Participating?
 
Bill-HansonConnect With a Mentor Author Commented:
Well, unfortunately I have not found an elegant solution for this.

I've tried using the NotesStream and NotesMimeEntity classes to do the work for me, but the results are not consistent across all platforms (I got different byte arrays using the same cypher-text when the characters were outside the ASCII range).

If anyone finds an elegant solution for this, please post it.  In the meantime, I'll post what I have.  This function will work for any string regardless of the characters contained in the string (works for characters that fall outside the ASCII range).
Declare Private Sub W32_CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" (destination As Any, Byval source As Any, Byval length As Long)
Public Function StringToByteArray(Byval text As String) As Variant
	'/**
	' * Converts a string to a byte array.
	' * @param text The string to convert.
	' * @return A byte array containing the charachter codes from the source text.
	' */
	Dim retval() As Byte
	Dim size As Long
	size = Len(text)
	Redim retval(0 To size-1)
	If (IsDefined("WIN32")) Then
		Call W32_CopyMemory(retval(0), Byval text, size)
	Else
		Dim i As Long
		For i = 0 To size - 1
			retval(i) = Asc(Mid$(text, i + 1, 1))
		Next
	End If
	StringToByteArray = retval
End Function

Open in new window

0
 
Sjef BosmanGroupware ConsultantCommented:
I suppose you shouldnt use Unicode, which is 2 byte code, but just plain Ascii. I had an example somewhere but I may be .... when I can find it back.

I also have been intending to use blowfish in a project. Here's an extract of the code:

Declare Sub bfinit Lib "blowfish.dll" Alias "bfinit" (Byval s As String, Byval l As Integer)
Declare Sub bfxcrypt Lib "blowfish.dll" Alias "bfxcrypt" (Byval s As String, Byval t As String, Byval l As Integer)
Declare Sub bfxdecrypt Lib "blowfish.dll" Alias "bfxdecrypt" (Byval s As String, Byval t As String, Byval l As Integer)

Again, if I can find those sources... Sheesh, what chaos... The general idea being: why on earth do you write it in LotusScript?? Ah, portability... You have a point there, although a .dll can be converted into a .so-file without a lot of trouble. Let me check my archives... I'll be back!
0
 
Sjef BosmanGroupware ConsultantCommented:
Yucky stream example:

      Dim filename As String
      On Error Resume Next
      filename = "C:\icon1.ico"    
      Kill filename
      On Error Goto 0
      If Not stream.Open( filename, "Binary") Then
            Messagebox "Export Icon: Cannot write picture " & filename
            Exit Sub
      End If              
      Dim buffer As Variant
      stream3.Position= 0
      Do
            buffer = stream3.Read(32767)
            Call stream.Write(buffer)
      Loop Until stream.IsEOS
      
      Call stream.Close()
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

 
Bill-HansonAuthor Commented:
Thanks for the "blowfish.dll" declarations, but I am trying to remain platform-independent.  I already have switches in the code to use RtlMoveMemory under Win32, but I wanted to make this fast for the other platforms as well.

Regarding the use of ASCII -- you can only specify the encoding when you're opening files (the WriteText method does not have a "charset" parameter).  The encoding defaults to "Unicode" for new objects, and the Charset property is read-only, so there's no way to change it unless you open a file (which would defeat my purpose).  That's why I explicitly used "Unicode" to read from the file -- so I could compare apples to apples.  Unfortunately for me, "String Apples" apparently taste differently from "File Apples".    :)
0
 
Sjef BosmanGroupware ConsultantCommented:
I checked some examples (on the Internet and elsewhere), but I think you'll have to accept that there's no way to create a binary stream.

Eh, did you try with the method Write instead of WriteText ?? I suppose that Read should be used to get the data out when Write was used to put the data in. And ReadText vs WriteText.
0
 
Bill-HansonAuthor Commented:
Thanks,

I tried using Write instead of WriteText, but Write  throws an error if you don't pass in a byte array.

I've given up on trying to get an unpadded array out of NotesStream, but there are other options.  Here's where I am on this:

- If WIN32 is defined, I use RtlMoveMemory to convert the string to a byte array.
- Otherwise, if the string length is > 16383, split the string into 16383 byte long chunks, process each chunk independently, return the results joined into a single Byte array.
- Otherwise, use NotesStream (WriteText->Read) to convert the sting to a byte array.

This should work well and support any string length.

My problem now is that I can't get Arrayappend to work on a byte array!

Man, it's always something!

To illustrate this LotusScript "bug", I created 2 agents (posted below).  The first one works, but the second one throws a Type Mismatch error on Arrayappend.

I already have a function that I use that wraps up Arrayappend to support null arrays, scalar arguments and objects.  I'd hate to have to add special-case handling for byte arrays as well.

Any thoughts on this issue?
Sub Initialize
	
	' This one works.
	Dim arr1(0) As Long
	Dim arr2(0) As Long
	Dim arr3 As Variant
	arr1(0) = 1
	arr2(0) = 2
	arr3 = Arrayappend(arr1, arr2)
	
End Sub
 
 
Sub Initialize
	
	' This one fails.
	Dim arr1(0) As Byte
	Dim arr2(0) As Byte
	Dim arr3 As Variant
	arr1(0) = 1
	arr2(0) = 2
	arr3 = Arrayappend(arr1, arr2) ' Type mismatch!
	
End Sub

Open in new window

0
 
Sjef BosmanGroupware ConsultantCommented:
Just a quick though: instead of ArrayAppend, can you use the Mid-function on the left of the equasion? Little-known feature...
0
 
Bill-HansonAuthor Commented:
I don't think so (unless I'm missing something).  I can't even save the agent.  This gives me "Type mismatch on: ARR1" on line 9:
Sub Initialize
	
	' This one fails.
	Dim arr1(0) As Byte
	Dim arr2(0) As Byte
	Dim arr3 As Variant
	arr1(0) = 1
	arr2(0) = 2
	arr3 = Mid(arr1, arr2)
	
End Sub

Open in new window

0
 
Sjef BosmanGroupware ConsultantCommented:
The other way round:

Sub Initialize
     
      ' This one fails.
      Dim arr1 As String*100
      Dim arr2 As String*100
      Dim arr3 As Variant
      arr1 = "1234"
      arr2= "5678"
      Mid(arr1, 4)= arr2  
     
End Sub

Check the example in the help. I think that this won't help you if you moved to Bytes all the way... Sorry if I lead you into the wrong direction.
0
 
Bill-HansonAuthor Commented:
No problem.  Yes, I need an actual Byte array so that the Blowfish algorithm can perform quickly.  Using Mid on Strings is too slow.  I have a working solution that I am finishing up.  I'll post the results soon.
0
 
Sjef BosmanGroupware ConsultantCommented:
There could be an elegant solution using the LCStream class. It's rather poorly documented, but I think it could do the conversion you need.
0
 
Bill-HansonAuthor Commented:
Thanks!

I'll put that on my list of things to try.
0
All Courses

From novice to tech pro — start learning today.