Web Listing 2: VarBinaryComp Implementation Imports System Imports System.Data Imports System.Data.SqlClient Imports System.Data.SqlTypes Imports Microsoft.SqlServer.Server Imports System.IO BEGIN CALLOUT A ' Declaration of the VarBinaryComp Structure. ' We implement the IBinarySerialize interface because of the fact that this ' structure uses "UserDefined" serialization _ _ Public Structure VarBinaryComp Implements INullable, Microsoft.SqlServer.Server.IBinarySerialize END CALLOUT A 'Private (member) variables used to store the actual compressed binary data ' (as a byte array) and the uncompressed length of the binary data Private m_Bytes As SqlBytes Private m_UnCompressedLength As SqlInt32 'Overloaded Constructor to Instantiate a new instance of VarBinaryComp ' Note: You cannot call an overloaded constructor from T-SQL code, but this ' constructor can be called by the type itself. For example, we call this ' constructor in several other functions (e.g. ParseVarBinaryU, ParseVarBinaryC) Public Sub New(ByVal flAlreadyCompressed As Boolean, ByVal sBytes As SqlBytes, ByVal UnCompressedLength As SqlInt32) Try 'If we've been passed an empty byte array, go ahead and exit. If sBytes Is Nothing Then Exit Sub 'If this byte array is not compressed, go ahead and compress it now. If flAlreadyCompressed = False Then m_Bytes = VarBinaryComp.Compress(sBytes) m_UnCompressedLength = sBytes.Value.Length Else 'The byte array has already been compressed. m_Bytes = sBytes 'Typically, we would want the application to pass in the UnCompressed Length so that 'we can optimize instantation. But, we will go ahead and relax this requirement a bit 'by allowing 0 to be passed in as the UnCompressedLength. If this is the case, we'll 'go ahead and UnCompress the bytes temporarily to determine the length If UnCompressedLength.Value = 0 Then m_UnCompressedLength = VarBinaryComp.DeCompress(sBytes).Value.Length Else m_UnCompressedLength = UnCompressedLength End If End If Catch ex As Exception Throw ex End Try End Sub BEGIN CALLOUT B 'UDTs must implement the ToString and Parse function to allow conversion ' to/from strings. With binary data, these functions provide little practical use. Public Overrides Function ToString() As String Try If m_Bytes Is Nothing Then Return String.Empty 'Convert Compressed Byte Array to String Return System.Text.Encoding.Unicode.GetString(m_Bytes.Value) Catch ex As Exception Throw ex End Try End Function END CALLOUT B BEGIN CALLOUT C 'Required property used to determine nullability Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull Get If m_Bytes Is Nothing Then Return True End If Return False End Get End Property 'Required function used to return a new, null instance of the UDT. Public Shared ReadOnly Property Null() As VarBinaryComp Get Return New VarBinaryComp(False, Nothing, 0) End Get End Property END CALLOUT C BEGIN CALLOUT D 'Example of a custom property (in this case, the uncompressed length of the ' binary data) Public ReadOnly Property UnCompressedLength() As SqlInt32 Get Return m_UnCompressedLength End Get End Property END CALLOUT D BEGIN CALLOUT E 'This custom shared function instantiates a new instance of VarBinaryComp, ' using uncompressed data as input. Public Shared Function ParseVarBinaryU(ByVal sUncompressedBytes As SqlBytes) As VarBinaryComp Try 'If we are passed in an empty byte array, return a Null instance If sUncompressedBytes Is Nothing Then Return VarBinaryComp.Null End If 'Otherwise, call the overloaded constructor to return a new instance Return New VarBinaryComp(False, sUncompressedBytes, 0) Catch ex As Exception Throw ex End Try End Function END CALLOUT E BEGIN CALLOUT F 'This custom shared function instantiates a new instance of VarBinaryComp, ' using data that is already compressed data as input. Public Shared Function ParseVarBinaryC(ByVal sCompressedBytes As SqlBytes, ByVal iUnCompressedLength As SqlInt32) As VarBinaryComp Try If sCompressedBytes.Value Is Nothing Then Return VarBinaryComp.Null End If Return New VarBinaryComp(True, sCompressedBytes, iUnCompressedLength) Catch ex As Exception Throw ex End Try End Function END CALLOUT F BEGIN CALLOUT G 'UDTs must implement the ToString and Parse function to allow conversion ' to/from strings. With binary data, these functions typically are of little ' value. Public Shared Function Parse(ByVal s As SqlString) As VarBinaryComp Try If s.IsNull Then Return VarBinaryComp.Null End If END CALLOUT G 'Convert String into Byte Array Dim bUncompressedBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(s.Value) Return New VarBinaryComp(False, New SqlBytes(bUncompressedBytes), 0) Catch ex As Exception Throw ex End Try End Function 'Custom property to get/set the compressed bytes Public Property CompressedBytes() As SqlBytes Get Return m_Bytes End Get Set(ByVal value As SqlBytes) m_Bytes = value End Set End Property 'Custom property to get/set the uncompressed bytes. If uncompressed bytes ' are passed in (i.e. Set), we will go ahead and compress them. Public Property UnCompressedBytes() As SqlBytes Get If m_Bytes Is Nothing Then Return Nothing Return VarBinaryComp.DeCompress(m_Bytes) End Get Set(ByVal value As SqlBytes) If value Is Nothing Then m_Bytes = Nothing Exit Property End If m_Bytes = VarBinaryComp.Compress(value) End Set End Property 'Custom shared function you can use to Compress Binary Data - without having 'to instantiate a VarBinaryComp UDT - or store the results in a VarBinaryComp UDT Public Shared Function Compress(ByVal UncompressedBytes As SqlBytes) As SqlBytes Try Dim output As New MemoryStream CompressWrapper.Compress(New MemoryStream(UncompressedBytes.Value), output, eCompressMethod.Deflate) Return New SqlBytes(output.ToArray) Catch ex As Exception Throw ex End Try End Function 'Custom shared function you can use to uncompress Binary Data - without having 'to instantiate a VarBinaryComp UDT - or store the results in a VarBinaryComp UDT Public Shared Function DeCompress(ByVal CompressedBytes As SqlBytes) As SqlBytes Try Dim output As New MemoryStream CompressWrapper.Decompress(New MemoryStream(CompressedBytes.Value), output, eCompressMethod.Deflate) Return New SqlBytes(output.ToArray) Catch ex As Exception Throw ex End Try End Function 'The Write function is part of the IBinarySerialize interface, which we use 'for customer serialization of the UDT Public Sub Write(ByVal w As System.IO.BinaryWriter) Implements Microsoft.SqlServer.Server.IBinarySerialize.Write Try 'Header Value will be used to detect null/not null Dim header As Byte If (Me.IsNull) Then header = 1 Else header = 0 End If w.Write(header) If header = 1 Then Return End If 'Write the UncompressedLength w.Write(m_UnCompressedLength.Value) 'Write the Compressed Byte Array w.Write(m_Bytes.Value) Catch ex As Exception Throw ex End Try End Sub 'The Read function is part of the IBinarySerialize interface, which we use 'for customer deserialization of the UDT Public Sub Read(ByVal r As System.IO.BinaryReader) Implements Microsoft.SqlServer.Server.IBinarySerialize.Read Try 'Inspect Header to determine null Dim header As Byte = r.ReadByte() If (header And 1) > 0 Then m_Bytes = Nothing m_UnCompressedLength = 0 Return End If 'Read the UnCompressedLength m_UnCompressedLength = r.ReadInt32 'Read the CompressedBytes m_Bytes = New SqlBytes(r.ReadBytes(CType(r.BaseStream.Length - 1, Integer))) Catch ex As Exception Throw ex End Try End Sub End Structure