Let’s imagine you need to read transparent file on the card but you don’t know its size in advance. Usual practice is analyzing response to select and extracting size information. In our test case we have to read PLMNSel file which contains Mobile Network Code and Mobile Country Code. Each PLMN information length is 3 bytes. Afterwards we can do something with this information but I skip it to show you the main idea. Let's have a look to the code:
package testappletwithbuffer; import sim.access.*; import javacard.framework.*; public class TestAppletWithBuffer extends javacard.framework.Applet { private SIMView gsmFile; private byte[] plmn; private byte[] response; protected TestAppletWithBuffer() { gsmFile = SIMSystem.getTheSIMView (); plmn = new byte[3]; response = new byte[15]; } public static void install(byte[] bArray, short bOffset, byte bLength){ TestAppletWithBuffer refApplet = new TestAppletWithBuffer(); refApplet.register(bArray, (short) (bOffset + 1), (byte) bArray[bOffset]); refApplet.readPLMNSel(); } public void process(APDU apdu) throws ISOException { // ignore the applet select command dispatched to the process if (selectingApplet()) { return; } } public void readPLMNSel() { gsmFile.select ((short) SIMView.FID_DF_GSM); gsmFile.select ((short) SIMView.FID_EF_PLMNSEL , response , (short) 0 , (short) response.length); short fileOffset = 0; short fileLength = Util.makeShort(response[2], response[3]); // reads the PLMN information to plmn buffer for (fileOffset = 0; fileOffset < fileLength; fileOffset += plmn.length) { gsmFile.readBinary (fileOffset , plmn , (short) 0 , (short) plmn.length); } } }
Let me show you how we can optimize this applet:
package testappletwithexception; import sim.access.*; import javacard.framework.*; public class TestAppletWithException extends javacard.framework.Applet { private SIMView gsmFile; private byte[] plmn; public TestAppletWithException() { gsmFile = SIMSystem.getTheSIMView(); plmn = new byte[3]; } public static void install(byte[] bArray, short bOffset, byte bLength) { TestAppletWithException refApplet = new TestAppletWithException(); refApplet.register(bArray, (short) (bOffset + 1), (byte) bArray[bOffset]); refApplet.readPLMNSel(); } public void process(APDU apdu) { // ignore the applet select command dispatched to the process if (selectingApplet()) { return; } } public void readPLMNSel() { short fileOffset = 0; gsmFile.select ((short) SIMView.FID_DF_GSM); gsmFile.select ((short) SIMView.FID_EF_PLMNSEL); try { // reads the PLMN information to plmn buffer for (fileOffset = 0; fileOffset < (short) 0xFFFF; fileOffset += plmn.length) { gsmFile.readBinary(fileOffset , plmn , (short) 0 , (short) plmn.length); } } catch (SIMViewException e) { // normal case } } }
Such kind of usage of exception could be weird to regular Java developers. But compare generated bytecodes:
Applet with exception | Applet with buffer |
.method public readPLMNSel()V 8 { .stack 5; .locals 1; L0: sconst_0; sstore_1; getfield_a_this 0; // ref gsmFile sspush 32544; invokeinterface 2 10 7; // SIMView getfield_a_this 0; // ref gsmFile sspush 28464; invokeinterface 2 10 7; // SIMView L1: sconst_0; sstore_1; goto L3; L2: getfield_a_this 0; // ref gsmFile sload_1; getfield_a_this 1; // ref plmn sconst_0; getfield_a_this 1; // ref plmn arraylength; invokeinterface 5 10 9; // SIMView pop; sload_1; getfield_a_this 1; // ref plmn arraylength; sadd; sstore_1; L3: sload_1; sconst_m1; if_scmplt L2; L4: goto L6; L5: pop; L6: return; .exceptionTable { // start_block end_block // handler_block catch_type_index L1 L4 L5 9; } } | .method public readPLMNSel()V 8 { .stack 5; .locals 2; L0: getfield_a_this 0; // ref gsmFile sspush 32544; invokeinterface 2 10 7; // SIMView getfield_a_this 0; // ref gsmFile sspush 28464; getfield_a_this 2; // ref response sconst_0; getfield_a_this 2; // ref response arraylength; invokeinterface 5 10 6; // SIMView pop; sconst_0; sstore_1; getfield_a_this 2; // ref response sconst_2; baload; getfield_a_this 2; // ref response sconst_3; baload; invokestatic 11; // makeShort sstore_2; sconst_0; sstore_1; goto L2; L1: getfield_a_this 0; // ref gsmFile sload_1; getfield_a_this 1; // ref plmn sconst_0; getfield_a_this 1; // ref plmn arraylength; invokeinterface 5 10 9; // SIMView pop; sload_1; getfield_a_this 1; // ref plmn arraylength; sadd; sstore_1; L2: sload_1; sload_2; if_scmplt L1; L3: return; } |
As you can see exception bytecode shorter and (here you have to trust me :)) it will work faster.
In given example bytecode size isn’t noticeably less but if you have more complex logic of verification it could make sense. If you compile to above two applets and compare size of IJC files it will be 339 and 363 bytes accordingly. So you will save 24 bytes and will win a little on speed.
Update:
After publishing post I figured out how to more optimize the version with exception. Actually we don't need to check anything at all and if we will replace for loop with while like below we will win even more on the size and speed:
while(true) { gsmFile.readBinary(fileOffset , plmn , (short) 0 , (short) plmn.length); fileOffset += plmn.length; }
And bytecode in this case will be very short:
.method public readPLMNSel()V 8 { .stack 5; .locals 1; L0: sconst_0; sstore_1; getfield_a_this 0; // ref gsmFile sspush 32544; invokeinterface 2 10 7; // sim/access/SIMView getfield_a_this 0; // ref gsmFile sspush 28464; invokeinterface 2 10 7; // sim/access/SIMView L1: getfield_a_this 0; // ref gsmFile sload_1; getfield_a_this 1; // ref plmn sconst_0; getfield_a_this 1; // ref plmn arraylength; invokeinterface 5 10 9; // sim/access/SIMView pop; sload_1; getfield_a_this 1; // ref plmn arraylength; sadd; sstore_1; goto L1; L2: pop; return; .exceptionTable { // start_block end_block handler_block catch_type_index L1 L2 L2 9; } }
So there is no limit for perfection :)
Update 2:
I've decided do not leave you with the statement "just trust me" concerning the speed. Let me explain why it is faster:
- There is no implicit garbage collection on JavaCard. You have to call it explicitly hence no need to unwind stack during exception
- Exceptions are not fully object as in terms of "big" Java. Usually it is just error code from native layer
As matter of fact usage of exceptions is cheap in JavaCard
1 comment:
Thanks! Looking forward to your new posts.
Post a Comment