Tech Articles


Fast String Concatenation


I needed a way to build a string in a loop for import into a DataWindow but speed was important.

I created an object with functions that allows for string concatenation using a blob variable that is allocated once and no external function calls are needed.

To test my object I concatenated a string of 445 characters to a new string 500 times. The standard method seen here took 320 milliseconds:

ls_data = "-start-"
For li_idx = 1 To 500
	ls_data = ls_data + "-" + String(li_idx) + "-"
	ls_data = ls_data + is_string
Next

The test using my object seen here took only 20 milliseconds:

n_stringclass sc

sc.alloc(225000)
sc.copy("-start-")
For li_idx = 1 To 500
	sc.concat("-" + String(li_idx) + "-")
	sc.concat(is_string)
Next
ls_data = sc.value()

The first step is to call the Alloc function passing the maximum size of the resulting string. It allocates a blob instance variable to that size. The Copy function copies your string to the beginning of the blob. The Concat function copies your string to the blob starting at the next position after the previous Copy/Concat call. The Value function returns a string from the current contents of the blob.

I also included a ReplaceAll function and one called Split which converts a deliminated string into an array.

Below is the object source, please try it yourself and let me know how it goes or if you have any suggestions.

$PBExportHeader$n_stringclass.sru
$PBExportComments$StringClass object
forward
global type n_stringclass from nonvisualobject
end type
end forward

global type n_stringclass from nonvisualobject autoinstantiate
end type

type prototypes

end prototypes

type variables
Private:

Blob iblob_string
Long il_position
Long il_size

end variables
forward prototypes
public subroutine copy (readonly string as_string)
public subroutine concat (readonly string as_string)
public function string value ()
public subroutine alloc (long al_size)
public function string replaceall (string as_oldstring, string as_findstr, string as_replace)
public function long split (string as_text, string as_sep, ref string as_array[])
public subroutine free ()
public function long size ()
end prototypes

public subroutine copy (readonly string as_string);// -----------------------------------------------------------------------------
// SCRIPT:		Copy
//
// PURPOSE:		This function moves the position to 1 and calls Concat so that
//					passed string is placed at the beginning.
//
// ARGUMENTS:  as_string	- The string to concatenate
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

il_position = 1

Concat(as_string)

end subroutine

public subroutine concat (readonly string as_string);// -----------------------------------------------------------------------------
// SCRIPT:		Concat
//
// PURPOSE:		This function concatenates the passed string to the instance
//					blob variable.
//
// ARGUMENTS:  as_string	- The string to concatenate
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

ULong lul_rtn

lul_rtn = BlobEdit(iblob_string, il_position, as_string)
If IsNull(lul_rtn) Then
	If Len(iblob_string) = 0 Then
		SignalError(20599, "String has not been allocated")
	Else
		SignalError(20599, "Allocated string size exceeded")
	End If
End If

il_position = il_position + (Len(as_string) * 2)

end subroutine

public function string value ();// -----------------------------------------------------------------------------
// SCRIPT:		Value
//
// PURPOSE:		This function returns the value from the blob instance variable.
//
//	RETURN:		Current string value
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

Return String(iblob_string)

end function

public subroutine alloc (long al_size);// -----------------------------------------------------------------------------
// SCRIPT:		Alloc
//
// PURPOSE:		This function allocates the blob instance variable. It needs
//					to be run once per instantiation.
//
// ARGUMENTS:  al_size	- The size of the allocated blob
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

If al_size > il_size Then
	iblob_string = Blob(Space(al_size))
	il_size = al_size
End If
il_position = 1

end subroutine

public function string replaceall (string as_oldstring, string as_findstr, string as_replace);// -----------------------------------------------------------------------------
// SCRIPT:		ReplaceAll
//
// PURPOSE:		This function replaces all occurrences of a string.
//
// ARGUMENTS:  as_oldstring	- The string to be modified
//					as_findstr		- The substring to be replaced
//					as_replace		- The new string to replace with
//
//	RETURN:		Updated string
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

String ls_newstring
Long ll_findstr, ll_replace, ll_pos

// get length of strings
ll_findstr = Len(as_findstr)
ll_replace = Len(as_replace)

// find first occurrence
ls_newstring = as_oldstring
ll_pos = Pos(ls_newstring, as_findstr)

Do While ll_pos > 0
	// replace old with new
	ls_newstring = Replace(ls_newstring, ll_pos, ll_findstr, as_replace)
	// find next occurrence
	ll_pos = Pos(ls_newstring, as_findstr, (ll_pos + ll_replace))
Loop

Return ls_newstring

end function

public function long split (string as_text, string as_sep, ref string as_array[]);// -----------------------------------------------------------------------------
// SCRIPT:     Split
//
// PURPOSE:    This function splits a string into an array.
//
// ARGUMENTS:  as_text	- The text to be split
//					as_sep	- The separator characters
//					as_array	- By ref output array
//
//	RETURN:		The number of items in the array
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

String ls_empty[], ls_work
Long ll_pos

as_array = ls_empty

If IsNull(as_text) Or as_text = "" Then Return 0

ll_pos = Pos(as_text, as_sep)
DO WHILE ll_pos > 0
	ls_work = Trim(Left(as_text, ll_pos - 1))
	as_text = Trim(Mid(as_text, ll_pos + Len(as_sep)))
	as_array[UpperBound(as_array) + 1] = ls_work
	ll_pos = Pos(as_text, as_sep)
LOOP
If Len(as_text) > 0 Then
	as_array[UpperBound(as_array) + 1] = as_text
End If

Return UpperBound(as_array)

end function

public subroutine free ();// -----------------------------------------------------------------------------
// SCRIPT:		Free
//
// PURPOSE:		This function initializes the blob instance variable.
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

SetNull(iblob_string)
il_position = 0

end subroutine

public function long size ();// -----------------------------------------------------------------------------
// SCRIPT:		Size
//
// PURPOSE:		This function returns the size of the blob instance variable.
//
//	RETURN:		Current blob value
//
// DATE        CHANGED BY	DESCRIPTION OF CHANGE / REASON
// ----------  ----------  -----------------------------------------------------
// 10/19/2016  RolandS		Initial creation
// -----------------------------------------------------------------------------

Return il_size

end function

on n_stringclass.create
call super::create
TriggerEvent( this, "constructor" )
end on

on n_stringclass.destroy
TriggerEvent( this, "destructor" )
call super::destroy
end on
Comments (0)
There are no comments posted here yet

Find Articles by Tag

Event PBVM RichTextEdit Control Linux OS Database Profile Charts Model Windows 10 Icons WinAPI Icon iOS SqlExecutor PDF SnapDevelop TreeView BLOB Filter Bug Azure UI Modernization Debugger Authorization Database Object TortoiseGit Visual Studio Jenkins SqlModelMapper Data Export JSON Interface Event Handling PostgreSQL Trial RESTClient .NET Assembly Git Event Handler Application RibbonBar Branch & Merge 32-bit Performance JSONGenerator IDE Database Table CI/CD SQL API CrypterObject License CoderObject XML Oracle Design Encoding Web Service Proxy ODBC TLS/SSL DevOps Authentication TFS Testing DLL DataWindow Outlook Database Table Data Windows OS MessageBox Service SDK Repository Error PBDOM Array RibbonBar Builder GhostScript JSON SOAP Source Code Database Transaction Resize Excel .NET Std Framework Elevate Conference Debug Sort Variable Encryption PowerServer Mobile Android File NativePDF Menu COM Platform PowerBuilder Compiler PBNI External Functions Source Control OrcaScript Import DataType Import JSON OAuth 2.0 PowerBuilder (Appeon) Text Mobile Deployment PowerScript (PS) PostgreSQL ODBC driver Database Table Schema UI Themes Automated Testing WebBrowser Debugging ActiveX Class SVN SnapObjects Database Connection .NET DataStore Export Graph Migration Web API DataWindow JSON Validation REST OAuth PDFlib Database Painter Open Source UI Expression Configuration PowerServer Web DragDrop OLE Stored Procedure SQL Server C# Window 64-bit JSONParser Installation Messagging Script Syntax InfoMaker PowerBuilder HTTPClient PFC