45 from pieces - create a writeStream, add the pieces (with #nextPut: or |
45 from pieces - create a writeStream, add the pieces (with #nextPut: or |
46 #nextPutAll:) and finally fetch the concatenated string via #contents. |
46 #nextPutAll:) and finally fetch the concatenated string via #contents. |
47 This is much better than constructing the big string by concatenating via |
47 This is much better than constructing the big string by concatenating via |
48 the comma (,) operator, since less intermediate garbage objects are created. |
48 the comma (,) operator, since less intermediate garbage objects are created. |
49 |
49 |
50 This implementation currently DOES change the |
50 This implementation currently DOES change the |
51 identity if the streamed-upon collection IF it cannot grow easily. |
51 identity if the streamed-upon collection IF it cannot grow easily. |
52 Collections which cannot grow easily are for example: Array, ByteArray and String. |
52 Collections which cannot grow easily are for example: Array, ByteArray and String. |
53 Thus it is slightly incompatible to ST-80 since 'aStream contents' does |
53 Thus it is slightly incompatible to ST-80 since 'aStream contents' does |
54 not always return the original collection. This may change. |
54 not always return the original collection. This may change. |
55 |
55 |
56 [caveat:] |
56 [caveat:] |
57 Basing capabilities like readability/writability/positionability/peekability on inheritance makes |
57 Basing capabilities like readability/writability/positionability/peekability on inheritance makes |
58 the class hierarchy ugly and leads to strange and hard to teach redefinitions (aka. NonPositionableStream |
58 the class hierarchy ugly and leads to strange and hard to teach redefinitions (aka. NonPositionableStream |
59 below PositionableStream or ExternalReadStream under WriteStream) |
59 below PositionableStream or ExternalReadStream under WriteStream) |
60 |
60 |
61 [author:] |
61 [author:] |
62 Claus Gittinger |
62 Claus Gittinger |
63 |
63 |
64 [see also:] |
64 [see also:] |
65 CharacterWriteStream (if streaming for a unicode string) |
65 CharacterWriteStream (if streaming for a unicode string) |
66 " |
66 " |
67 ! |
67 ! |
68 |
68 |
69 examples |
69 examples |
70 " |
70 " |
366 |
366 |
367 coll = __INST(collection); |
367 coll = __INST(collection); |
368 p = __INST(position); |
368 p = __INST(position); |
369 |
369 |
370 if (__isNonNilObject(coll) && __isSmallInteger(p)) { |
370 if (__isNonNilObject(coll) && __isSmallInteger(p)) { |
371 pos = __intVal(p); |
371 pos = __intVal(p); |
372 /* make 1-based */ |
372 /* make 1-based */ |
373 pos = pos + 1; |
373 pos = pos + 1; |
374 wL = __INST(writeLimit); |
374 wL = __INST(writeLimit); |
375 |
375 |
376 if ((wL == nil) |
376 if ((wL == nil) |
377 || (__isSmallInteger(wL) && (pos <= __intVal(wL)))) { |
377 || (__isSmallInteger(wL) && (pos <= __intVal(wL)))) { |
378 OBJ cls; |
378 OBJ cls; |
379 |
379 |
380 cls = __qClass(coll); |
380 cls = __qClass(coll); |
381 |
381 |
382 rL = __INST(readLimit); |
382 rL = __INST(readLimit); |
383 if (__isSmallInteger(rL)) { |
383 if (__isSmallInteger(rL)) { |
384 __readLimit = __intVal(rL); |
384 __readLimit = __intVal(rL); |
385 } |
385 } |
386 |
386 |
387 if (cls == @global(String)) { |
387 if (cls == @global(String)) { |
388 if (__isCharacter(anObject) |
388 if (__isCharacter(anObject) |
389 && ((ch = __intVal(__characterVal(anObject))) <= 255) /* ch is unsigned */ |
389 && ((ch = __intVal(__characterVal(anObject))) <= 255) /* ch is unsigned */ |
390 && (pos <= __stringSize(coll))) { |
390 && (pos <= __stringSize(coll))) { |
391 __StringInstPtr(coll)->s_element[pos-1] = ch; |
391 __StringInstPtr(coll)->s_element[pos-1] = ch; |
392 advancePositionAndReturn: ; |
392 advancePositionAndReturn: ; |
393 if ((__readLimit >= 0) && (pos >= __readLimit)) { |
393 if ((__readLimit >= 0) && (pos >= __readLimit)) { |
394 __INST(readLimit) = __mkSmallInteger(pos); |
394 __INST(readLimit) = __mkSmallInteger(pos); |
395 } |
395 } |
396 __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1); |
396 __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1); |
397 RETURN ( anObject ); |
397 RETURN ( anObject ); |
398 } |
398 } |
399 } else if (cls == @global(ByteArray)) { |
399 } else if (cls == @global(ByteArray)) { |
400 if (__isSmallInteger(anObject) |
400 if (__isSmallInteger(anObject) |
401 && ((ch = __intVal(anObject)) <= 0xFF) /* ch is unsigned */ |
401 && ((ch = __intVal(anObject)) <= 0xFF) /* ch is unsigned */ |
402 && (pos <= __byteArraySize(coll))) { |
402 && (pos <= __byteArraySize(coll))) { |
403 __ByteArrayInstPtr(coll)->ba_element[pos-1] = ch; |
403 __ByteArrayInstPtr(coll)->ba_element[pos-1] = ch; |
404 goto advancePositionAndReturn; |
404 goto advancePositionAndReturn; |
405 } |
405 } |
406 } else if (cls == @global(Array)) { |
406 } else if (cls == @global(Array)) { |
407 if (pos <= __arraySize(coll)) { |
407 if (pos <= __arraySize(coll)) { |
408 __ArrayInstPtr(coll)->a_element[pos-1] = anObject; |
408 __ArrayInstPtr(coll)->a_element[pos-1] = anObject; |
409 __STORE(coll, anObject); |
409 __STORE(coll, anObject); |
410 goto advancePositionAndReturn; |
410 goto advancePositionAndReturn; |
411 } |
411 } |
412 } else if (cls == @global(Unicode16String)) { |
412 } else if (cls == @global(Unicode16String)) { |
413 if (__isCharacter(anObject) |
413 if (__isCharacter(anObject) |
414 && ((ch = __intVal(__characterVal(anObject))) <= 0xFFFF) /* ch is unsigned */ |
414 && ((ch = __intVal(__characterVal(anObject))) <= 0xFFFF) /* ch is unsigned */ |
415 && (pos <= __unicode16StringSize(coll))) { |
415 && (pos <= __unicode16StringSize(coll))) { |
416 __Unicode16StringInstPtr(coll)->s_element[pos-1] = ch; |
416 __Unicode16StringInstPtr(coll)->s_element[pos-1] = ch; |
417 goto advancePositionAndReturn; |
417 goto advancePositionAndReturn; |
418 } |
418 } |
419 } else if (cls == @global(Unicode32String)) { |
419 } else if (cls == @global(Unicode32String)) { |
420 if (__isCharacter(anObject) |
420 if (__isCharacter(anObject) |
421 && (pos <= __unicode32StringSize(coll))) { |
421 && (pos <= __unicode32StringSize(coll))) { |
422 __Unicode32StringInstPtr(coll)->s_element[pos-1] = __intVal(__characterVal(anObject)); |
422 __Unicode32StringInstPtr(coll)->s_element[pos-1] = __intVal(__characterVal(anObject)); |
423 goto advancePositionAndReturn; |
423 goto advancePositionAndReturn; |
424 } |
424 } |
425 } |
425 } |
426 } |
426 } |
427 } |
427 } |
428 #endif |
428 #endif |
429 %}. |
429 %}. |
430 (writeLimit isNil |
430 (writeLimit isNil |
431 or:[(position + 1) <= writeLimit]) ifTrue:[ |
431 or:[(position + 1) <= writeLimit]) ifTrue:[ |
432 (position >= collection size) ifTrue:[self growCollection]. |
432 (position >= collection size) ifTrue:[self growCollection]. |
433 collection at:(position + 1) put:anObject. |
433 collection at:(position + 1) put:anObject. |
434 (position >= readLimit) ifTrue:[readLimit := (position + 1)]. |
434 (position >= readLimit) ifTrue:[readLimit := (position + 1)]. |
435 position := position + 1. |
435 position := position + 1. |
436 ] ifFalse:[ |
436 ] ifFalse:[ |
437 WriteError raiseErrorString:'write beyond writeLimit' |
437 WriteError raiseErrorString:'write beyond writeLimit' |
438 ]. |
438 ]. |
439 ^anObject |
439 ^anObject |
440 ! |
440 ! |
441 |
441 |
442 nextPutAll:aCollection |
442 nextPutAll:aCollection |
445 instead a single grow on the final size is performed." |
445 instead a single grow on the final size is performed." |
446 |
446 |
447 |nMore "{ Class: SmallInteger }" |
447 |nMore "{ Class: SmallInteger }" |
448 final "{ Class: SmallInteger }" | |
448 final "{ Class: SmallInteger }" | |
449 |
449 |
450 (collection isNil or:[aCollection isSequenceable not]) ifTrue:[ |
450 (collection notNil and:[aCollection isSequenceable]) ifFalse:[ |
451 "/ fallback |
451 "/ fallback |
452 super nextPutAll:aCollection. |
452 super nextPutAll:aCollection. |
453 ^ self. |
453 ^ self. |
454 ]. |
454 ]. |
455 |
455 |
456 nMore := aCollection size. |
456 nMore := aCollection size. |
457 nMore == 0 ifTrue:[ |
457 nMore == 0 ifTrue:[ |
458 "/ for the programmer.. |
458 "/ for the programmer.. |
459 aCollection isCollection ifFalse:[ |
459 aCollection isCollection ifFalse:[ |
460 self error:'invalid argument (not a collection)' mayProceed:true |
460 self error:'invalid argument (not a collection)' mayProceed:true |
461 ]. |
461 ]. |
462 ]. |
462 ]. |
463 |
463 |
464 final := position + nMore. |
464 final := position + nMore. |
465 (writeLimit notNil |
465 (writeLimit notNil |
466 and:[final > writeLimit]) ifTrue:[ |
466 and:[final > writeLimit]) ifTrue:[ |
467 final := writeLimit. |
467 final := writeLimit. |
468 nMore := final - position |
468 nMore := final - position |
469 ]. |
469 ]. |
470 (final > collection size) ifTrue:[ |
470 (final > collection size) ifTrue:[ |
471 self growCollection:final |
471 self growCollection:final |
472 ]. |
472 ]. |
473 collection |
473 collection |
474 replaceFrom:(position + 1) |
474 replaceFrom:(position + 1) |
475 to:final |
475 to:final |
476 with:aCollection |
476 with:aCollection |
477 startingAt:1. |
477 startingAt:1. |
478 |
478 |
479 (position >= readLimit) ifTrue:[readLimit := position]. |
479 (position >= readLimit) ifTrue:[readLimit := position]. |
480 position := position + nMore. |
480 position := position + nMore. |
481 "/ ^ aCollection -- self |
481 "/ ^ aCollection -- self |
482 |
482 |
489 instead a single grow on the final size is performed." |
489 instead a single grow on the final size is performed." |
490 |
490 |
491 |nMore final| |
491 |nMore final| |
492 |
492 |
493 collection isNil ifTrue:[ |
493 collection isNil ifTrue:[ |
494 ^ super nextPutAll:aCollection startingAt:pos1 to:pos2 |
494 ^ super nextPutAll:aCollection startingAt:pos1 to:pos2 |
495 ]. |
495 ]. |
496 |
496 |
497 nMore := pos2 - pos1 + 1. |
497 nMore := pos2 - pos1 + 1. |
498 final := position + nMore. |
498 final := position + nMore. |
499 (writeLimit notNil |
499 (writeLimit notNil |
500 and:[final > writeLimit]) ifTrue:[ |
500 and:[final > writeLimit]) ifTrue:[ |
501 final := writeLimit. |
501 final := writeLimit. |
502 nMore := final - position |
502 nMore := final - position |
503 ]. |
503 ]. |
504 (final > collection size) ifTrue:[ |
504 (final > collection size) ifTrue:[ |
505 self growCollection:final |
505 self growCollection:final |
506 ]. |
506 ]. |
507 |
507 |
508 collection replaceFrom:position + 1 |
508 collection replaceFrom:position + 1 |
509 to:final |
509 to:final |
510 with:aCollection |
510 with:aCollection |
511 startingAt:pos1. |
511 startingAt:pos1. |
512 |
512 |
513 position := position + nMore. |
513 position := position + nMore. |
514 (position >= readLimit) ifTrue:[readLimit := position]. |
514 (position >= readLimit) ifTrue:[readLimit := position]. |
515 "/ ^ aCollection -- return self |
515 "/ ^ aCollection -- return self |
516 |
516 |
528 |
528 |
529 nextPutAllUnicode:aString |
529 nextPutAllUnicode:aString |
530 "normal streams can not handle multi-byte characters, so convert them to utf8" |
530 "normal streams can not handle multi-byte characters, so convert them to utf8" |
531 |
531 |
532 "this code is not perfect if you use both #nextPutAll: and #nextPutAllUnicode: |
532 "this code is not perfect if you use both #nextPutAll: and #nextPutAllUnicode: |
533 with the same stream, since 8-bit characters (with the highest bits set) |
533 with the same stream, since 8-bit characters (with the highest bits set) |
534 are not stored as UTF, so we get some inconsistent string" |
534 are not stored as UTF, so we get some inconsistent string" |
535 |
535 |
536 collection isString ifTrue:[ |
536 collection isString ifTrue:[ |
537 collection bitsPerCharacter == 16 ifTrue:[ |
537 collection bitsPerCharacter == 16 ifTrue:[ |
538 self nextPutAllUtf16:aString. |
538 self nextPutAllUtf16:aString. |
539 ] ifFalse:[ |
539 ] ifFalse:[ |
540 self nextPutAllUtf8:aString. |
540 self nextPutAllUtf8:aString. |
541 ]. |
541 ]. |
542 ] ifFalse:[ |
542 ] ifFalse:[ |
543 self nextPutAll:aString |
543 self nextPutAll:aString |
544 ]. |
544 ]. |
545 |
545 |
546 "Modified: / 28-09-2011 / 16:15:52 / cg" |
546 "Modified: / 28-09-2011 / 16:15:52 / cg" |
547 ! |
547 ! |
548 |
548 |
560 |
560 |
561 coll = __INST(collection); |
561 coll = __INST(collection); |
562 p = __INST(position); |
562 p = __INST(position); |
563 |
563 |
564 if (__isNonNilObject(coll) && __isSmallInteger(p) && __isSmallInteger(anObject)) { |
564 if (__isNonNilObject(coll) && __isSmallInteger(p) && __isSmallInteger(anObject)) { |
565 unsigned ch; |
565 unsigned ch; |
566 |
566 |
567 ch = __intVal(anObject); |
567 ch = __intVal(anObject); |
568 |
568 |
569 pos = __intVal(p); |
569 pos = __intVal(p); |
570 /* make 1-based */ |
570 /* make 1-based */ |
571 pos = pos + 1 - __intVal( @global(PositionableStream:ZeroPosition)); |
571 pos = pos + 1 - __intVal( @global(PositionableStream:ZeroPosition)); |
572 wL = __INST(writeLimit); |
572 wL = __INST(writeLimit); |
573 |
573 |
574 if ((wL == nil) |
574 if ((wL == nil) |
575 || (__isSmallInteger(wL) && (pos <= __intVal(wL)))) { |
575 || (__isSmallInteger(wL) && (pos <= __intVal(wL)))) { |
576 OBJ cls; |
576 OBJ cls; |
577 |
577 |
578 cls = __qClass(coll); |
578 cls = __qClass(coll); |
579 |
579 |
580 rL = __INST(readLimit); |
580 rL = __INST(readLimit); |
581 if (__isSmallInteger(rL)) { |
581 if (__isSmallInteger(rL)) { |
582 __readLimit = __intVal(rL); |
582 __readLimit = __intVal(rL); |
583 } |
583 } |
584 |
584 |
585 if (cls == @global(String)) { |
585 if (cls == @global(String)) { |
586 if ((pos <= __stringSize(coll)) |
586 if ((pos <= __stringSize(coll)) |
587 && (ch <= 0xFF)) { /* ch is unsigned */ |
587 && (ch <= 0xFF)) { /* ch is unsigned */ |
588 __StringInstPtr(coll)->s_element[pos-1] = ch; |
588 __StringInstPtr(coll)->s_element[pos-1] = ch; |
589 advancePositionAndReturn: ; |
589 advancePositionAndReturn: ; |
590 if ((__readLimit >= 0) && (pos >= __readLimit)) { |
590 if ((__readLimit >= 0) && (pos >= __readLimit)) { |
591 __INST(readLimit) = __mkSmallInteger(pos); |
591 __INST(readLimit) = __mkSmallInteger(pos); |
592 } |
592 } |
593 __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1); |
593 __INST(position) = __mkSmallInteger(__intVal(__INST(position)) + 1); |
594 RETURN ( anObject ); |
594 RETURN ( anObject ); |
595 } |
595 } |
596 } else if (cls == @global(ByteArray)) { |
596 } else if (cls == @global(ByteArray)) { |
597 if ((pos <= __byteArraySize(coll)) |
597 if ((pos <= __byteArraySize(coll)) |
598 && (ch <= 0xFF)) { /* ch is unsigned */ |
598 && (ch <= 0xFF)) { /* ch is unsigned */ |
599 __ByteArrayInstPtr(coll)->ba_element[pos-1] = ch; |
599 __ByteArrayInstPtr(coll)->ba_element[pos-1] = ch; |
600 goto advancePositionAndReturn; |
600 goto advancePositionAndReturn; |
601 } |
601 } |
602 } |
602 } |
603 } |
603 } |
604 } |
604 } |
605 #endif |
605 #endif |
606 %}. |
606 %}. |
607 ^ super nextPutByte:anObject |
607 ^ super nextPutByte:anObject |
608 ! |
608 ! |
609 |
609 |
610 nextPutBytes:count from:anObject startingAt:start |
610 nextPutBytes:count from:anObject startingAt:start |
611 "write count bytes from an object starting at index start. |
611 "write count bytes from an object starting at index start. |
612 Return the number of bytes written. |
612 Return the number of bytes written. |
613 The object must have non-pointer indexed instvars |
613 The object must have non-pointer indexed instvars |
614 (i.e. be a ByteArray, String, Float- or DoubleArray). |
614 (i.e. be a ByteArray, String, Float- or DoubleArray). |
615 Use with care - non object oriented i/o. |
615 Use with care - non object oriented i/o. |
616 This is provided for compatibility with externalStream; |
616 This is provided for compatibility with externalStream; |
617 to support binary storage" |
617 to support binary storage" |
618 |
618 |
619 anObject isByteCollection ifTrue:[ |
619 anObject isByteCollection ifTrue:[ |
620 self nextPutAll:anObject startingAt:start to:(start + count - 1). |
620 self nextPutAll:anObject startingAt:start to:(start + count - 1). |
621 ^ count. |
621 ^ count. |
622 ]. |
622 ]. |
623 ^ super nextPutBytes:count from:anObject startingAt:start |
623 ^ super nextPutBytes:count from:anObject startingAt:start |
624 ! |
624 ! |
625 |
625 |
626 nextPutUnicode:aCharacter |
626 nextPutUnicode:aCharacter |
627 "normal streams can not handle multi-byte characters, so convert them to utf8" |
627 "normal streams can not handle multi-byte characters, so convert them to utf8" |
628 |
628 |
629 "this code is not perfect if you use both #nextPut: and #nextPutUnicode: |
629 "this code is not perfect if you use both #nextPut: and #nextPutUnicode: |
630 with the same stream, since 8-bit characters (with the highest bits set) |
630 with the same stream, since 8-bit characters (with the highest bits set) |
631 are not stored as UTF, so we get some inconsistent string" |
631 are not stored as UTF, so we get some inconsistent string" |
632 |
632 |
633 collection isString ifTrue:[ |
633 collection isString ifTrue:[ |
634 collection bitsPerCharacter == 16 ifTrue:[ |
634 collection bitsPerCharacter == 16 ifTrue:[ |
635 self nextPutUtf16:aCharacter. |
635 self nextPutUtf16:aCharacter. |
636 ] ifFalse:[ |
636 ] ifFalse:[ |
637 self nextPutUtf8:aCharacter. |
637 self nextPutUtf8:aCharacter. |
638 ]. |
638 ]. |
639 ] ifFalse:[ |
639 ] ifFalse:[ |
640 self nextPut:aCharacter. |
640 self nextPut:aCharacter. |
641 ]. |
641 ]. |
642 ! ! |
642 ! ! |
643 |
643 |
644 !WriteStream class methodsFor:'documentation'! |
644 !WriteStream class methodsFor:'documentation'! |
645 |
645 |
646 version |
646 version |
647 ^ '$Header: /cvs/stx/stx/libbasic/WriteStream.st,v 1.96 2015-03-26 23:32:34 cg Exp $' |
647 ^ '$Header: /cvs/stx/stx/libbasic/WriteStream.st,v 1.97 2015-04-27 17:04:19 cg Exp $' |
648 ! |
648 ! |
649 |
649 |
650 version_CVS |
650 version_CVS |
651 ^ '$Header: /cvs/stx/stx/libbasic/WriteStream.st,v 1.96 2015-03-26 23:32:34 cg Exp $' |
651 ^ '$Header: /cvs/stx/stx/libbasic/WriteStream.st,v 1.97 2015-04-27 17:04:19 cg Exp $' |
652 ! ! |
652 ! ! |
653 |
|