String Slicing

For scripting examples for learning how to use AMXX.
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

String Slicing

Post by Archive-Poster »

An important optimization that is a little counter intuitive is PAWN string slicing, best shown in the following example:

SUMMARY: By passing an indexed array (my_array[5] instead of my_array) into a function or native which accepts an array, the index you pass in becomes the base of the array the function you called accepts.

So, if you pass in my_array[5] to my_func, then when my_func does your_array[0], it is the value of my_array[5].

LONGER EXPLANATION:

Code: Select all

public test()
{
	new str2[100] = "y halo thar"
	test2(str2[2]);
}

// This shows that its not a fluke or a compiler optimization
public test2(str[])
{
	test3(str)
}

public test3(str[])
{
        // Writes "halo thar" to file
	write_file("addons/amxmodx/test.ini",str);
}
To most, this would be assumed to be a compiler error. After all, it looks as if you are simply handing test2 a cell indexed FROM the string, not a string itself!

This is a primitive form of "array slicing", which can be done with any kind of array. It lets you cut out a subset of the array by telling where you want the array to start at when you pass it. True array slicing can do a lot more, but this is the same amount that C affords us natively using pointers, so we can't be picky.

USES:

Why is this useful? Well, for one thing, it can allow us to do a lot of operations without calls to natives. For example, usually when you want a subset of the string, you have to use copyc or format to get it out, therefore using a native call AND allocating a buffer onto the stack (costly due to zeroing).

However, what if you don't REALLY want a new copy; it'd be perfectly fine if the old copy was messed with and mangled, you just don't want the front part? Or, what if you aren't even going to edit the string; you just need to cut a part out? In that case, string slicing is the only real way to do it, unless you want to go to costly natives like format. Here is a peice of code which makes this easy to understand:

Code: Select all

new str_to_cut[] = "Cut us here| Woah important info"

stock testfunc(str[])
{
	new i = containi(str,"|")
	parse_me(str[i])
}

stock parse_me(str[])
{
	// Do stuff on string AFTER pipe
}
We still use a str function: containi. However, to do stuff in "parse" normally, we'd have to pass in i, and do all offsets based on i. This is extremely cost ineffecient, ugly to look at, and hard to understand (and thus error prone). The string slicing method is a lot cleaner.

Obviously, there are better examples; for instance, the one I am quite fond of is for using in set_task. Instead of using explode (costly) or strtok (costly), if we assume we don't really need nor care about keeping the integrity of the string (which is almost always the case; you use parts of the string, then you don't use them again, therefore you can ruinate them all you want) then the string slicing alternative becomes a lot more cost efficient.

Another example is when you wish to stuff non-string data at the beginning of a string, for space or efficiency reasons. In some of my newer code, it is often the case that stuffing a few integer values at the beginning of a string is more portable than enum based structs, and a lot simpler to understand in code; what was perhaps 4 integers and a string being passed into another plugin, saved somewhere, only to be passed back without ever being parsed, is now one array. Quite a lot easier on the eyes.

A full explanation of how string slicing works is attached to this post, if you care to read it; I do not guarantee its validity, as it is quite early in the morning.
Attachments
string slicing.txt.zip
(1.24 KiB) Downloaded 21 times
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

I didn't know it, but then I am quite new.

I've never really dealt with pointers before, though.
Though I do understand them.

I've never really seen them because I'm used to Caché which allows for indirection, so it doesn't need pointers.
(that is, referencing a variable (or function) by name, where the name is stored in another variable)

e.g.

Code: Select all

set x="y"
set y="hello"
write @x 
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

but also fundamentally less powerful.
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

I assume pointers are pointers directly to memory, then?

So you can pass them between programs and still access the same data?

If that's the case, then pointers pwn indirection.

But I still love indirection ... it also allows for a lot that pointers don't (in the same way)

e.g.

Code: Select all

set x="y+10"
set y=7
write @x 
would, of course, write 17 :-D

Still, someone get us back on-topic ...
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

It's been a while since I looked into amxx source code so I could be wrong about this: doesn't amxx declare the buffer for string manipulations as static?!? If that's true then the main cost would be the actual native call.

Anyways this is usefull if the target position is known, but if you want a subset starting after a certain character then you would still have to use natives ( or not ).
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

wut

If you're talking about how the core and modules read a string from a Pawn script, then yes and no, although declaring a heavily used variable static doesn't have as huge of a performance benefit in C as it does with Pawn - the string buffer is declared static internally for convenience.

However, Pawn stores 1 character from each string in one cell. One character in a C-style string is 1 byte long, there are (normally) 4 bytes in one cell - the extra 3 bytes are wasted. The routine to read a Pawn string inside of core or a module has to basically make its own copy of the string, and manipulate it from there.

However, that is assuming the string needs to be passed to a routine expecting a C-style string, a lot of the natives are written to read and change the string as a Pawn style string, so it doesn't have to do the fairly expensive copy back - the inclusion of formatex() was an example of this optimization.
User avatar
Archive-Poster
Posts: 1249
Gender:
United States of America

Re: String Slicing

Post by Archive-Poster »

Another use for this technique is to store multiple items of data (strings and/or integers) in one variable. This is useful when you're only able to pass one array to a function or native, such as set_task(). This removes the need for EngFunc_AllocString or variables of global scope. It should be obvious how, but if you want an example, just ask.
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests