Seek and position in with-sd-card


#1

I have the need to open a file on an sd-card and seek to a specific position and read 128 bytes of data. Then possibly seek ahead to a different position on the file.

I see that the SD.h library has the file.seek() and file.position() functionality available. I’m curious if we could use the unused address parameter of the with-sd-card stream to achieve this functionality, or if there is a better way to add file-position in ulisp, such as a function that could work on the stream itself to set the position.


#2

I think you’d have to provide them as two new functions that take the SD card stream as a parameter.


#3

(file-position stream) and (file-position stream position) would be how it’s done in Common Lisp, so that sounds reasonable. I’m not sure how to get access to the SDgfile from a passed stream, and I’d rather not repeatedly flush the buffer to get to the desired position.

I guess since SDgfile is a global variable, one could just run SDgfile.seek(position) directly. Perhaps after checking that the stream is the correct type, and that the position given is less than the size of the file that is currently open. Something like the following:

object *fn_file_position(object *args, object *env) {
  (void) env;
  #if defined(sdcardsupport)
  if (isstream(first(args))>>8 != SDSTREAM) error2("invalid SD file stream");
  if (second(args) != NULL) {
    unsigned long pos = (unsigned long)(checkinteger(second(args)));
    if (pos <= SDgfile.size()) {
       SDgfile.seek(pos);  //TODO: check for write mode, use SDpfile instead
       return number(pos);
    }
  } else {
    return number(SDgfile.position()); //TODO: check for write mode?
    }
  #endif
  return nil;
}

const char string_file_position[] PROGMEM = "file-position";
//...
const char doc_file_position[] PROGMEM = "(file-position stream)\n (file-position stream position)\n Sets with-sd-card stream's file position, or returns it current value";
//...
{ string_file_position, fn_file_position, 0212, doc_file_position },

Unfortunately the above code fails to return a file position when no second argument is given…and doesn’t always work when you give a position. I think I’m messing up the passing of position to and from lisp objects, as it’s expecting unsigned longs.

If you have any suggestions, I’d love to hear them. Otherwise I’ll try and work on this more when I have time.


#4

OK, I’ll be happy to have a look at your code some time in the next day or so.


#5

PS What platform are you working on? I’ll try and test it on the same platform.


#6

I’m using the T-Deck currently.

I believe that the problem has to do with my lack of knowledge at how to pass unsigned long position variables to and from the uLisp form and the file.seek() and file.position() C++ methods. I’m currently using checkinteger() and number() to do these transformations, but I believe it’s reducing the numbers to fewer bytes.

An example of how I’m testing:
(with-sd-card (str "test.txt" 1 ) (dotimes (x 100) (print x str)))

Then try and seek through the file:
(with-sd-card (str "test.txt") (dotimes (x 100) (print x)(file-position str x)(princ (read-line str)))) <-- this works, but I may need to have a better idea of how to pass the file position.

(with-sd-card (str "test.txt") (dotimes (x 10) (princ (file-position str)) (princ (read-line str)))) <-- this fails horribly.


#7

I think the only small error in your function is the test for a second argument. In C the test:

if (second(args) != NULL) {

isn’t safe because, if there isn’t a second argument, it’s looking beyond the extent of the list into unknown territory. Your function would work provided you called it with:

(file-position str nil)

to read the file position. The correct test is:

  if (cdr(args) != NULL) { // Second argument
    SDgfile.seek(checkinteger(second(args)));
    return second(args);
  } else {
    return number(SDgfile.position());
  }

Also, your code doesn’t seem to do anything if you try to seek beyond the end of the file. I did a few tests and SDgfile.seek() seems quite happy to position the file pointer beyond the end of the file, so I’m not sure what the correct action should be. In any case Lisp should return something.

Regards, David


#8

YES! That seems to fix the problem! Thanks so very much.

I am doing a check to see that the position given is smaller than the total file size before doing the seek:

unsigned long pos = (unsigned long)(checkinteger(second(args)));
    if (pos <= SDgfile.size()) {

I’ll do some checks to see if these are in the same units. I seem to remember seeing code that used file.seek(file.filesize()) to get to the end of the file in the past on other projects.

Thanks again for your help.


#9

My point was that your fn_file_position() function doesn’t return anything if the pos <= SDgfile.size() test fails. All uLisp functions need to return a value.


#10

Huh, I assumed that the final return nil; at the end of the function would catch that condition.


#11

Yes, sorry, I missed that.


#12

@johnsondavies Is there any way to determine the mode that a with-sd-card form is being used (READ, WRITE, APPEND), so that one could use the .seek() functionality for both WRITE mode along with READ, ie switch between using SDgfile and SDpfile. Is there a means of determining this from the stream variable?

I don’t currently have this need, but I could see it being useful in the future. Thanks again for all your help.


#13

In uLisp a new stream is created with stream(streamtype, address), where address is an 8-bit address to allow you to have multiple streams of the same type, such as I2C streams where address is the I2C address.

Currently with SD streams the address is always 1, but the address could be set to the mode (READ, APPEND, WRITE) in sp_withsdcard(), which would provide the information you need. I don’t believe this would affect any other aspects of the SD card interface.